From f53d6d1fc1d6bc26c8dce4fc5f1030e25ef341b8 Mon Sep 17 00:00:00 2001 From: hexxone Date: Wed, 18 Nov 2020 23:17:13 +0100 Subject: [PATCH 01/76] Add TypeScript, Remove Detector --- Detector.js | 40 --- ShaderQuality.js | 3 + src/ReloadHelper.ts | 80 ++++++ src/Stats.ts | 24 ++ src/WEAS.ts | 102 ++++++++ src/WEICUE.ts | 497 +++++++++++++++++++++++++++++++++++++ src/WarnHelper.ts | 580 ++++++++++++++++++++++++++++++++++++++++++++ wewwa.js | 13 +- 8 files changed, 1297 insertions(+), 42 deletions(-) delete mode 100644 Detector.js create mode 100644 src/ReloadHelper.ts create mode 100644 src/Stats.ts create mode 100644 src/WEAS.ts create mode 100644 src/WEICUE.ts create mode 100644 src/WarnHelper.ts diff --git a/Detector.js b/Detector.js deleted file mode 100644 index 6684c43..0000000 --- a/Detector.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @author alteredq / http://alteredqualia.com/ - * @author mr.doob / http://mrdoob.com/ - */ -Detector = { - canvas: !!window.CanvasRenderingContext2D, - webgl: (function () { try { return !!window.WebGLRenderingContext && !!document.createElement('canvas').getContext('experimental-webgl'); } catch (e) { return false; } })(), - workers: !!window.Worker, - fileapi: window.File && window.FileReader && window.FileList && window.Blob, - getWebGLErrorMessage: function () { - var domElement = document.createElement('div'); - domElement.style.fontFamily = 'monospace'; - domElement.style.fontSize = '13px'; - domElement.style.textAlign = 'center'; - domElement.style.background = '#eee'; - domElement.style.color = '#000'; - domElement.style.padding = '1em'; - domElement.style.width = '475px'; - domElement.style.margin = '5em auto 0'; - if (!this.webgl) { - domElement.innerHTML = window.WebGLRenderingContext ? [ - 'Your graphics card does not seem to support WebGL.
', - 'Find out more at http://get.webgl.org/' - ].join('\n') : [ - 'Your browser does not seem to support WebGL.
', - 'Find out how to get it at http://get.webgl.org/' - ].join('\n'); - } - return domElement; - }, - addGetWebGLMessage: function (parameters) { - var parent, id, domElement; - parameters = parameters || {}; - parent = parameters.parent !== undefined ? parameters.parent : document.body; - id = parameters.id !== undefined ? parameters.id : 'oldie'; - domElement = Detector.getWebGLErrorMessage(); - domElement.id = id; - parent.appendChild(domElement); - } -}; diff --git a/ShaderQuality.js b/ShaderQuality.js index e43ea39..7677ff1 100644 --- a/ShaderQuality.js +++ b/ShaderQuality.js @@ -9,6 +9,9 @@ * @description * helper for globally injecting "precision" strings into THREE.js shaders. * + * @todo + * - check if three.js overrides this? + * - add "default" option -> choose highest available */ ShaderQuality = { diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts new file mode 100644 index 0000000..81c3617 --- /dev/null +++ b/src/ReloadHelper.ts @@ -0,0 +1,80 @@ +/** + * @author D.Thiele @https://hexx.one + */ + +export class ReloadHelper { + + waitSeconds = 3; + + constructor() { + $(() => { + this.injectCSS(); + this.injectHTML(); + }); + } + + injectCSS() { + var st = document.createElement("style"); + st.innerHTML = ` + #reload-bar { + position: absolute; + opacity: 0; + top: 0px; + height: 10px; + width: 0%; + background-color: #989a; + transition: all ` + this.waitSeconds + `s ease, opacity 0.33s ease; + } + #reload-bar.show { + opacity: 1; + width: 100%; + background-color: #e11a; + } + #reload-bar.done { + transition: opacity 0.33s ease; + } + #reload-text { + position: absolute; + top: -6em; + width: 100%; + text-align: center; + font-weight: 100; + font-size: 3em; + color: #fffa; + transition: all .33s ease, color ` + this.waitSeconds + `s ease, text-shadow ` + this.waitSeconds + `s ease; + } + #reload-text.show { + top: 10px; + color: #e11a; + text-shadow: 0 0 20px rgba(255, 50, 50, .5), 0 0 15px rgba(255, 50, 50, .5); + } + #reload-text.done { + transition: position 0.33s linear; + } + #reload-text { + text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5); + } + `; + document.head.append(st); + } + + injectHTML() { + var outer = document.createElement("div"); + outer.id = "reloader"; + var bar = document.createElement("div"); + bar.id = "reload-bar"; + var tex = document.createElement("h1"); + tex.id = "reload-text"; + tex.innerHTML = "Reload"; + outer.append(bar, tex); + document.body.append(outer); + } + + Show() { + $("#reload-bar, #reload-text").removeClass("done").addClass("show"); + } + + Hide() { + $("#reload-bar, #reload-text").removeClass("show").addClass("done"); + } +}; \ No newline at end of file diff --git a/src/Stats.ts b/src/Stats.ts new file mode 100644 index 0000000..47b9de3 --- /dev/null +++ b/src/Stats.ts @@ -0,0 +1,24 @@ +declare interface Stats { + REVISION: number; + dom: HTMLDivElement; + addPanel( panel: Stats.Panel ): Stats.Panel; + showPanel( id: number ): void; + begin(): void; + end(): void; + update(): void; + domElement: HTMLDivElement; + setMode( id: number ): void; +} + +declare function Stats(): Stats; + +declare namespace Stats { + interface Panel { + dom: HTMLCanvasElement; + update( value: number, maxValue: number ): void; + } + + function Panel( name?: string, fg?: string, bg?: string ): Panel; +} + +export default Stats; \ No newline at end of file diff --git a/src/WEAS.ts b/src/WEAS.ts new file mode 100644 index 0000000..b3beb19 --- /dev/null +++ b/src/WEAS.ts @@ -0,0 +1,102 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * WEWWA + * Wallpaper Engine Audio Supplier + * + * DEPENDS ON: + * - "./worker/weasWorker.js" + * - jQuery (window loaded event) + * - Wallpaper Engine Web Wallpaper environment + * - audio-processing supported wallpaper... + * + * This is an aditional JS file to be included in any Wallpaper Engine + * Web-Wallpaper project to make working with audio easier. + * It will automatically start to receive and process the audio data + * which can then be accessed on the global object. + * +*/ + +export class WEAS { + + // has currently valid audio data stored? + hasAudio() { + // return false if there is no data or its invalid due to time (> 3s old) + return this.lastAudio && !this.lastAudio.silent && + (performance.now() / 1000 - this.lastAudio.time < 3); + } + // audio processing worker + weasWorker = null; + + // last processed audio object + lastAudio = null; + + // settings object + settings = { + audioprocessing: true, + // do pink-noise processing? + equalize: true, + // convert to mono? + mono_audio: true, + // invert low & high freqs? + audio_direction: 0, + // peak filtering + peak_filter: 1, + // neighbour-smoothing value + value_smoothing: 2, + // time-value smoothing ratio + audio_increase: 75, + audio_decrease: 35, + // multipliers + treble_multiplier: 0.5, + mids_multiplier: 0.75, + bass_multiplier: 1.8, + // ignore value leveling for "silent" data + minimum_volume: 0.005, + } + + constructor() { + // if wallpaper engine context given, listen + if (!window['wallpaperRegisterAudioListener']) { + console.log("'window.wallpaperRegisterAudioListener' not given!"); + return; + } + + // initialize web worker + this.weasWorker = new Worker('./js/worker/weasWorker.js'); + + // worker event data + this.weasWorker.addEventListener("message", (e) => { + e.data.data = new Float64Array(e.data.data); + this.lastAudio = e.data; + }, true); + + // worker Error + this.weasWorker.addEventListener("error", (e) => { + console.log("weas error: [" + e.filename + ", Line: " + e.lineno + "] " + e.message); + }, true); + + // intialize wallpaper engine audio listener when laoded + $(() => window['wallpaperRegisterAudioListener']((audioArray) => { + // check proof + if (!audioArray) return; + if (audioArray.length != 128) { + console.log("audioListener: received invalid audio data array. Length: " + audioArray.length); + return; + } + let audBuff = new Float64Array(audioArray); + // post web worker task + this.weasWorker.postMessage({ + settings: this.settings, + last: this.lastAudio, + audio: audBuff.buffer + }, [audBuff.buffer]); + })); + } +} \ No newline at end of file diff --git a/src/WEICUE.ts b/src/WEICUE.ts new file mode 100644 index 0000000..bc3f7b8 --- /dev/null +++ b/src/WEICUE.ts @@ -0,0 +1,497 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @see + * REQUIRES: + * - jQuery + * + * @description + * WEICUE + * Wallpaper Engine iCUE effects for web wallpapers + * + * Uses several different methods to create + * Lighting effects for Corsair ICUE devices. + */ + +import { WEAS } from "./WEAS"; + +export class WEICUE { + + weas : WEAS = null; + // runtime values + PAUSED = false; + isAvailable = false; + canvasX = 23; + canvasY = 7; + icueDevices = []; + preview = null; + icueInterval = null; + helperCanvas = null; + helperContext = null; + + // settings + settings = { + icue_mode: 1, + icue_area_xoff: 50, + icue_area_yoff: 90, + icue_area_width: 75, + icue_area_height: 30, + icue_area_blur: 5, + icue_area_decay: 15, + icue_area_preview: false, + icue_main_color: "0 0.8 0", + // AudiOrbits bg Color, used as "decay"-color + main_color: "0 0 0", + } + + constructor(weas: WEAS) { + this.weas = weas; + + // Plugin handler + window['wallpaperPluginListener'] = { + onPluginLoaded: function (name, version) { + console.log("Plugin: " + name + ", Version: " + version); + if (name === 'cue') this.isAvailable = true; + if (name === 'led') this.isAvailable = true; + } + } + $(() => { + // inject helpers + this.injectCSS(); + this.injectHTML(); + this.init(); + }); + } + + injectCSS() { + var st = document.createElement("style"); + st.innerHTML = ` + #icueholder { + position: absolute; + top: -120px; + left: 0; + width: auto; + height: auto; + margin: 10px; + display: none; + } + #icuelogo { + float: left; + height: 80px; + width: 80px; + } + #icuetext { + float: left; + margin: 25px 5px; + font-size: 175%; + } + #icueholder { + text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5); + } + .cuePreview { + position: absolute; + background: rgba(255, 0, 0, .3); + } + `; + document.head.append(st); + } + + injectHTML() { + var outer = document.createElement("div"); + outer.id = "icueholder"; + var imgg = document.createElement("img"); + imgg.id = "icuelogo"; + imgg.setAttribute("src", ` +data:image/png;base64,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 +AAAASUVORK5CYII= + `); + // make text holder + var txtt = document.createElement("div"); + txtt.id = "icuetext"; + // append image and text + outer.append(imgg, txtt); + document.body.append(outer); + } + + // show a message by icue + icueMessage(msg) { + $("#icueholder").css('opacity', 1); + $("#icuetext").html(msg); + $("#icueholder").fadeIn({ queue: false, duration: "slow" }); + $("#icueholder").animate({ top: "0px" }, "slow"); + setTimeout(() => { + $("#icueholder").fadeOut({ queue: false, duration: "slow" }); + $("#icueholder").animate({ top: "-120px" }, "slow"); + }, 10000); + } + + // helper + getArea(inPx?) { + var sett = this.settings; + var wwid = window.innerWidth; + var whei = window.innerHeight; + var w = wwid * sett.icue_area_width / 100; + var h = whei * sett.icue_area_height / 100; + var l = ((wwid - w) * sett.icue_area_xoff / 100); + var t = ((whei - h) * sett.icue_area_yoff / 100); + return { + width: w + (inPx ? "px" : ""), + height: h + (inPx ? "px" : ""), + left: l + (inPx ? "px" : ""), + top: t + (inPx ? "px" : ""), + } + } + + // get data for icue + getEncodedCanvasImageData(imageData) { + var colorArray = []; + for (var d = 0; d < imageData.data.length; d += 4) { + var write = d / 4 * 3; + colorArray[write] = imageData.data[d]; + colorArray[write + 1] = imageData.data[d + 1]; + colorArray[write + 2] = imageData.data[d + 2]; + } + return String.fromCharCode.apply(null, colorArray); + } + + // canvas blur helper function + gBlurCanvas(canvas, ctx, blur) { + var sum = 0; + var delta = 5; + var alpha_left = 1 / (2 * Math.PI * delta * delta); + var step = blur < 3 ? 1 : 2; + + var x, weight; + for (var y = -blur; y <= blur; y += step) { + for (x = -blur; x <= blur; x += step) { + weight = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta)); + sum += weight; + } + } + for (var y = -blur; y <= blur; y += step) { + for (x = -blur; x <= blur; x += step) { + ctx.globalAlpha = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta)) / sum * blur * blur; + ctx.drawImage(canvas, x, y); + } + } + ctx.globalAlpha = 1; + } + + // show or hide preview + updatePreview() { + var sett = this.settings; + // create preview? + if (!this.preview && sett.icue_area_preview) { + this.preview = document.createElement("div"); + this.preview.classList.add("cuePreview"); + document.body.appendChild(this.preview); + } + // update settings or destroy + if (this.preview) { + if (!sett.icue_area_preview) { + document.body.removeChild(this.preview); + this.preview = null; + } + else Object.apply(this.preview.style, this.getArea(true)); + } + } + + init() { + var sett = this.settings; + // dont initialize if disabled + if (sett.icue_mode == 0) return; + + this.showWaiting(); + setTimeout(() => { + this.hideWaiting(); + }, 30000); + + this.initCUE(0); + + console.log("weiCUE: init..."); + + // recreate if reinit + if (this.icueInterval) clearInterval(this.icueInterval); + if (this.helperCanvas) document.body.removeChild(this.helperCanvas); + // setup canvas + this.helperCanvas = document.createElement("canvas"); + this.helperCanvas.id = "helpCvs"; + this.helperCanvas.width = this.canvasX; + this.helperCanvas.height = this.canvasY; + this.helperCanvas.style.display = "none"; + this.helperContext = this.helperCanvas.getContext("2d"); + document.body.appendChild(this.helperCanvas); + + // update devices about every 33ms/30fps. iCue doesnt really support higher values + this.icueInterval = setInterval(this.updateFrame, 1000 / 30); + } + + // will initialize ICUE api & usage + initCUE(count) { + // wait for plugins + if (!this.isAvailable) { + if (count < 100) { + $("#icueholder").animate({ 'opacity': 0.1 + (100 - count) / 115 }, 250); + setTimeout(() => this.initCUE(count + 1), 300); + } + else this.icueMessage("LED: Plugin not found!"); + return; + } + // setup devices + this.icueDevices = []; + + window['cue'].getDeviceCount((deviceCount) => { + this.icueMessage("LED: Found " + deviceCount + " devices."); + for (var xi = 0; xi < deviceCount; xi++) { + var xl = xi; + window['cue'].getDeviceInfo(xl, (info) => { + info.id = xl; + window['cue'].getLedPositionsByDeviceIndex(xl, function (leds) { + info.leds = leds; + this.icueDevices[xl] = info; + }); + }); + } + }); + } + + // do the thing... + updateFrame() { + var sett = this.settings; + if (this.PAUSED || !this.isAvailable || sett.icue_mode == 0 || this.icueDevices.length < 1) return; + // projection mode + if (sett.icue_mode == 1) { + // get scaled down image data and encode it for icue + var encDat = this.getEncodedCanvasImageData(this.helperContext.getImageData(0, 0, this.canvasX, this.canvasY)); + // update all icueDevices with data + for (var xi = 0; xi < this.icueDevices.length; xi++) { + window['cue'].setLedColorsByImageData(xi, encDat, this.canvasX, this.canvasY); + } + } + // color mode + if (sett.icue_mode == 2) { + // get lol objects + var col = sett.icue_main_color.split(" ") as unknown[]; + var ledColor = { + r: col[0] as number * 255, + g: col[1] as number * 255, + b: col[2] as number * 255 + };; + // try audio multiplier processing + if (this.weas.hasAudio()) { + var aud = this.weas.lastAudio; + var mlt = 255 * aud.average / aud.range / aud.intensity * 10; + ledColor = { + r: Math.min(255, Math.max(0, col[0] as number * mlt)), + g: Math.min(255, Math.max(0, col[1] as number * mlt)), + b: Math.min(255, Math.max(0, col[2] as number * mlt)) + }; + } + // update all icueDevices with data + for (var xi = 0; xi < this.icueDevices.length; xi++) { + window['cue'].setAllLedsColorsAsync(xi, ledColor); + } + } + } + + // prepare canvas + updateCanvas(mainCanvas) { + var sett = this.settings; + if (!this.isAvailable || !mainCanvas || sett.icue_mode == 0 || this.icueDevices.length < 1) return; + + if (sett.icue_mode == 1) { + // get helper vars + var cueWid = this.canvasX; + var cueHei = this.canvasY; + var area = this.getArea(); + var hctx = this.helperContext; + // get real rgb values + var spl = sett.main_color.split(' ') as unknown[]; + for (var i = 0; i < spl.length; i++) spl[i] = (spl[i] as number * 255); + // overlay "decay" style + hctx.fillStyle = "rgba(" + spl.join(", ") + ", " + sett.icue_area_decay / 100 + ")"; + hctx.fillRect(0, 0, cueWid, cueHei); + // scale down and copy the image to the helper canvas + hctx.drawImage(mainCanvas, area.left, area.top, area.width, area.height, 0, 0, cueWid, cueHei); + // blur the helper projection canvas + if (sett.icue_area_blur > 0) this.gBlurCanvas(this.helperCanvas, hctx, sett.icue_area_blur); + } + } + + showWaiting() { + $("#icuetext").html("LED: waiting for plugin."); + $("#icueholder").fadeIn({ queue: false, duration: "fast" }); + $("#icueholder").animate({ top: "0px" }, "fast"); + } + + hideWaiting() { + $("#icueholder").fadeOut({ queue: false, duration: "fast" }); + $("#icueholder").animate({ top: "-120px" }, "fast"); + } +} diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts new file mode 100644 index 0000000..8173d82 --- /dev/null +++ b/src/WarnHelper.ts @@ -0,0 +1,580 @@ +/** + * @author D.Thiele @https://hexx.one + */ + +export class WarnHelper { + + animationSeconds = 1; + waitSeconds = 10; + + constructor() { + $(() => { + this.injectCSS(); + this.injectHTML(); + }); + } + + injectCSS() { + var st = document.createElement("style"); + st.innerHTML = ` + #triggerwarn { + object-fit: contain; + max-height: 30vmax; + top: 25vmin; + opacity: 0; + transition: opacity ` + this.animationSeconds + `s ease; + } + #triggerwarn.show { + opacity: 1; + } + `; + document.head.append(st); + } + + injectHTML() { + var outer = document.createElement("img"); + outer.id = "triggerwarn"; + outer.setAttribute("src", ` + data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAACXBIWXMAAC4jAAAuIwF4pT92AAAgAElEQVR42uzdeXxdV3kv/N+z1h7OP +GiWrFmeB1me7dhxnLEkIZCRDCSEoe1bejsAvS2l7Xtf3r4tLfe2ty/QXmjhlgJJSBiSQKAEaCg0TCEhEJoQEkhIQuI4nmXLGs7Ze6/7xz5HOjpny7ZkyZbi3/fz0cfW +3jqD1tlaaz9reBZARERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE +RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE +RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERHNLWAS00PwLttqNq3IDyXT8LbbjDBSD4LnDhdHPPNuW+t5TzZkjIyMjhU9+8pM+S4qIiB +ZoO6eyfcmObH3qFsd1L4JgZHBs7L7dKfWVHy9u3jtcGCsMDg56d999t2Fp0UKkWQS0kNyP7ZJYm++rW9Tw7u7ezqs7Otq68vW5xWKpvhGYw0cyiSFxneLy5cu9n/zkJ +wFLjIiIFprfXLE6l27N/1Znb8dvdHW3L2toqu92Ys7yMT8IDqfcg0Ei5lmWVezs7AyeeuopFhgxACGaS9fmuzP57oY3LGpveWt9Jltnaa0dy3Icy2oaOjaSO+CqY4W4 +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 +di3qNQBUtnMmCAJ/bGws+MQnPsF2joiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIi +IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI +iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIvqGMJ +AAAIABJREFUiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiICDDGbDSzq6Xiub9d +cXzHKb7Pf6t4rl385E663B6vKLfl0z1PRHSm6+SztR5ju8e/I6IyxSIgIqJp3GCcb4z5MN87P3MiopmyXoW/01EA3zrO+RSADRXfPwggOM7PF3iZEBFvQk0OwD8CeAO +A7/C98zMnImIAUiIiTwHYdZwKdQDAjyoOXSIio2fgfV7Ey4+IFpDFpRvRV+V7Z5089585y/jVj58xnSxOwSIiIiIiIgYgRERERETEAISIiIiIiIgByJlijLnWGPMFY8 +xuY8yYMeZFY8zXjDFvMsbo4zzupFLVGWNeY4z5pDHmF8aYYWPMSMVrvNsY0zjD9/0fFa9/9XF+7k0VP3fYGKOO87PfqvjZ7il+5jxjzAeMMY8aY/YYYwrGmCPGmOeMM +Z83xrzVGOPM4edVTqU8WvreNsb8rjHmB8aYQ8aYUWPM08aYjxhjVk/jeU/p95rD99VkjPm/jTHfMca8Unpfe4wx3zDGvMMYkzzB4x8pva8XS9+/1hjz/dK1+JIx5h5j +zAUzKP/9pe+1MebNxph/L72/4dK1/lFjzIqqxy4zxvyDMebnpb+DQ8aYB40xv3686/JUPyNjzEUV1/U+Y4x1Eq/1RxWP+Yt5co32GWPeb4x52BhzsPT7v1wq+z8sLTi +OetxvGWMMgIcrDm+v+P2+PUfXXrkcDpe+F2PMDcaYL/8f9s47zK6q6sPvnkloAYHQIl2KIoLSRJQeLDSRJl2kCggWFBQhE4ZMwicI+uFnAZQSBAQE6TU0KYKAdFDASq +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 +ieJWgnmOPkWSBJinVcC7eVcX7fshw74jiGYKK/r/22v8AxEeMoUqzlRjEB42Ffqd7ARrNJg8b33aGGxpMdIwdlCT/vMMk/cF5pI6qiOPa+oi9bAydhTPHp7GN/Tru5F +n7nEnyb9xiqkE5cM3cRz8TDC37d+QSzyF+NsaVrIVFGOnuPYF6Lx3zYgLGGf7an5um8BiqKq1rmxR9gWIsIFYyKIiIiEmYJmohnJsW71LVkHowXxZWISIZ+ARERERER +ETVAREREREREDRARERERERE1QERERERERA0QERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE +RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE +RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER +ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE +RERERERERERERERERERERERERERERERERERERERERERERkQZmZteZ2fVFljnNonvJrzPOv18mwjHc4pddQTHSEGmiU3yY2X3+fa9c73+kYdTPh8EtSsc1D+urfLgWK6 +f28cu9bmaLKORKCuOGy9NdlcdC165+XRFuuc7RzIaa2aDuUhZ287ywUFh397LdzEaZ2VNmFlNpV10K0PIT5e7AQGB4kUXfAx7J+pvjv3sy6/PnFLIiUkUnADOBA8zsV +3nKsl7AWYABRzjnvlewyQ/kOj0IuBDoo9BQWJfpDGA14GjFsjRCRutpZh+Y2fllrv9SoV84SvkFRBo+regXkIXD5Ad1168bhPcwH94vm1k8x/cJ//1FCq3K8/iPOY91 +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 +re7WZ57Du/zRf96IH3+vXfz5rDIjvcIpfxBcIj+xw7hlR/28ySZta/yPqR03Qp6azEeCrlWpf3+lrgHMu5DkQpB3OGdY44qcl1qIR8V1b5aWZ/0zwg0giNkJfN7I1Gb +IB03Ckxs8m+C8McP1zeMP8zeJTji7S+rzyc7ycVmusLqFFZ478XLCD9cHpn+4vT976QvirX8JNRl61iA8SFCsqfZn3eMcHRZrVogJQYDyua2YU+Hr73w5r+O9ds7FGX +rWIYruiHrp7qL7ZPmNm2fj6VWypNx377V/qLzlzfPWGUmfX220x3zHMROqdNfTx869e7IMccB5GXLTFtlnK8dW+AlJGeBpjZ4z58pvu4XsavG3Um9CjlSDnxXEnjuZT +9LZSna5F3u1Eem+kn3rsvlC4uy1HG5Aq3yNeDKA0Q/9k5/pjMzAZG2EakNF1qOosaTyWWJyU3QMq8DkQtBxcK6zxxUpPrUMT0XG4D5G4/bHgvRLqwAXJwtcfnF5Ga59 +txhRr+5S4rIvJjLzN/4OGwgv9lrE2ponr0DEh5rgE+Ag5XUIiIiIj8YB1MMJDAOQoKNUC6lB8J6e/AQVH6pYqIiIhI92JmfXx9r9U/RC9qgHR5I+QW4DbgFIWGiIiIy +A/O/yMYMvwMBYWIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI +iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiEg +B/x9OcNpH2VDkjgAAAABJRU5ErkJggg== + `); + document.body.append(outer); + } + + Show(callback) { + // show it + $("#triggerwarn").addClass("show"); + // wait some time + setTimeout(() => { + // hide it & wait again + $("#triggerwarn").removeClass("show"); + setTimeout(callback, this.animationSeconds * 1000); + }, this.waitSeconds * 1000); + } +}; \ No newline at end of file diff --git a/wewwa.js b/wewwa.js index fbe5a2b..66502a9 100644 --- a/wewwa.js +++ b/wewwa.js @@ -565,9 +565,18 @@ wewwApp.UpdateSettings = () => { var partials = cprop.split(/&&|\|\|/); // loop all partial values of the check for (var part of partials) { + var prefix ="wewwaProps."; var onlyVal = part.match(/[a-zA-Z0-9_\.]*/)[0]; - if (!onlyVal.startsWith("wewwaProps.")) - cprop = cprop.replace(onlyVal, "wewwaProps." + onlyVal); + if (!onlyVal.startsWith(prefix) && !onlyVal.startsWith("!" + prefix)){ + // fix for inverted values + var replW = onlyVal; + if(replW.startsWith("!")) { + replW = replW.substr(1); + prefix = "!" + prefix; + } + cprop = cprop.replace(onlyVal, prefix + replW); + } + } visible = eval(cprop) == true; //console.log("eval: (" + cprop + ")=" + visible + " for: " + p); From 3344609c453d8b7a197fde6dc4a518496c590619 Mon Sep 17 00:00:00 2001 From: hexxone Date: Thu, 19 Nov 2020 17:25:36 +0100 Subject: [PATCH 02/76] Remove old JS files, add new TS logger --- ReloadHelper.js | 80 ------- WarnHelper.js | 584 ------------------------------------------------ src/Smallog.ts | 42 ++++ src/WEAS.ts | 8 +- src/WEICUE.ts | 6 +- weas.js | 97 -------- weicue.js | 495 ---------------------------------------- 7 files changed, 51 insertions(+), 1261 deletions(-) delete mode 100644 ReloadHelper.js delete mode 100644 WarnHelper.js create mode 100644 src/Smallog.ts delete mode 100644 weas.js delete mode 100644 weicue.js diff --git a/ReloadHelper.js b/ReloadHelper.js deleted file mode 100644 index 0d5cd85..0000000 --- a/ReloadHelper.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - */ - -var ReloadHelper = { - - waitSeconds: 3, - injected: false, - - injectCSS: function() { - var st = document.createElement("style"); - st.innerHTML = ` - #reload-bar { - position: absolute; - opacity: 0; - top: 0px; - height: 10px; - width: 0%; - background-color: #989a; - transition: all ` + this.waitSeconds + `s ease, opacity 0.33s ease; - } - #reload-bar.show { - opacity: 1; - width: 100%; - background-color: #e11a; - } - #reload-bar.done { - transition: opacity 0.33s ease; - } - #reload-text { - position: absolute; - top: -6em; - width: 100%; - text-align: center; - font-weight: 100; - font-size: 3em; - color: #fffa; - transition: all .33s ease, color ` + this.waitSeconds + `s ease, text-shadow ` + this.waitSeconds + `s ease; - } - #reload-text.show { - top: 10px; - color: #e11a; - text-shadow: 0 0 20px rgba(255, 50, 50, .5), 0 0 15px rgba(255, 50, 50, .5); - } - #reload-text.done { - transition: position 0.33s linear; - } - #reload-text { - text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5); - } - `; - document.head.append(st); - }, - - injectHTML: function() { - var outer = document.createElement("div"); - outer.id = "reloader"; - var bar = document.createElement("div"); - bar.id = "reload-bar"; - var tex = document.createElement("h1"); - tex.id = "reload-text"; - tex.innerHTML = "Reload"; - outer.append(bar, tex); - document.body.append(outer); - }, - - Show: function() { - var self = ReloadHelper; - if(!self.injected) { - self.injected = true; - self.injectCSS(); - self.injectHTML(); - } - $("#reload-bar, #reload-text").removeClass("done").addClass("show"); - }, - - Hide: function() { - $("#reload-bar, #reload-text").removeClass("show").addClass("done"); - } -}; \ No newline at end of file diff --git a/WarnHelper.js b/WarnHelper.js deleted file mode 100644 index 6a4d238..0000000 --- a/WarnHelper.js +++ /dev/null @@ -1,584 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - */ - -var WarnHelper = { - - animationSeconds: 1, - waitSeconds: 10, - injected: false, - - injectCSS: function() { - var st = document.createElement("style"); - st.innerHTML = ` - #triggerwarn { - object-fit: contain; - max-height: 30vmax; - top: 25vmin; - opacity: 0; - transition: opacity ` + this.animationSeconds + `s ease; - } - #triggerwarn.show { - opacity: 1; - } - `; - document.head.append(st); - }, - - injectHTML: function() { - var outer = document.createElement("img"); - outer.id = "triggerwarn"; - outer.setAttribute("src", ` - data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAACXBIWXMAAC4jAAAuIwF4pT92AAAgAElEQVR42uzdeXxdV3kv/N+z1h7OP -GiWrFmeB1me7dhxnLEkIZCRDCSEoe1bejsAvS2l7Xtf3r4tLfe2ty/QXmjhlgJJSBiSQKAEaCg0TCEhEJoQEkhIQuI4nmXLGs7Ze6/7xz5HOjpny7ZkyZbi3/fz0cfW -3jqD1tlaaz9reBZARERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER -ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE -RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER -ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE -RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER -ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERHNLWAS00PwLttqNq3IDyXT8LbbjDBSD4LnDhdHPPNuW+t5TzZkjIyMjhU9+8pM+S4qIiB -ZoO6eyfcmObH3qFsd1L4JgZHBs7L7dKfWVHy9u3jtcGCsMDg56d999t2Fp0UKkWQS0kNyP7ZJYm++rW9Tw7u7ezqs7Otq68vW5xWKpvhGYw0cyiSFxneLy5cu9n/zkJ -wFLjIiIFprfXLE6l27N/1Znb8dvdHW3L2toqu92Ys7yMT8IDqfcg0Ei5lmWVezs7AyeeuopFhgxACGaS9fmuzP57oY3LGpveWt9Jltnaa0dy3Icy2oaOjaSO+CqY4W4 -O6y0Gurr6ys+8cQT7B0iIqIF457cDivekNjV3rvo95vq6/tc27ZsbVmubeeLntd0wC8Wj6XjQ6LVMREZe+yxx9jZRgxAiObKHe5mK720fqC+ueGPWhobeixtKQAQEdi -WpYNisX7QK/hH4vbhwNKHlFLDjz76qMeSIyKiBdLOiduWaK9rb3xHW0vTOTHXsaU0W97SWklgcsOFMfegowc91z6olDq6fPny4k9+8hMWHi0oikVAC0WqN5tJ5VLXND -XWL7e1NSl41kpJSz6X7ipgY3bM67eU6nJdN33rrbcyyCYiogUh1p60E3Wpi5pbmrbHY25cKpbqigjy6ZTTpd2VTcOFDbbBYtu287FYzGbJ0ULDmzNaEO6u2+EkG1IXt -Xa2/lZ9LteqlZqcQMEYaEBpz48dPjLkH046r/iWPiAiR/v6+rwnnniChUhERPPWZzM7JJaw+1v72v5LU33dOsd2au7RFCCWMe7wkSHroKP2F1x7P0QOr1ixovDYY49x -yjEtGBwBoXnvdmwVuynVkmmqu7E+n+vRSqnq4AO+D+X7qLed+FJfVjQeK/Tborpt286m02kG2kRENK+ZmEpnOhouq8vnt7quY0e1c+J5SGvL6oXV3TFUWOsGZollWQ2 -xWMxhCRIDEKJZlGhzXDflXNTc3LAp5roxEZmokIMA8LzxL9dA2u1YffuhYwOxor/M0lar4zjxG264gSmniYhoXvp8YquO1cfWNjbVX5JJJfNK1OR2zvfH2znLD9Aciy -c6joytzowUV1pKdTiOk37Tm97EzjZiAEI0Gz6bOUespLOsub3punQq2aFVRaXs+0CxCBSLkKIHKXhQno8MxOkbxZKW/Uc32JA+27brk8kk58gSEdH8lLCamjubX5/Np -DfYVphgZVInW7mtK3iQood4YHRXoBd17Du6Me4Fy23LaonH4+61117LzjZiAEJ0yhdoSqXTLfkr6vK5ja5jWxOBhzcp+ECxNApS9OAERlpF53oGR9akRgsrtVJtsVgs -+aY3vYnXOxHRNBjAMcBOA/yJAT5jgEcMsMcARw3gGWDIAK8Y4BkDfMsAHzPAu0uPYcfPSbhTb3TSvfVbsrnMa1PJREKAmsADxTDwKB9Tvo9GbcV7j4ytyA+NrrZEuhz -HyWYyGY6CEAMQolPxtdxOHc8nN7UsarosnUzWKYOJYejyqEc5ECkUIYUipFiEhKMgVk8BnW0Hjqx3ArPUtqyGeDzuvBp6hwzQYoDAhP1jlV/XzOJruAY4VvX8owZIzN -LzL414/8YAq+aozN4Z8VpjBqg/jZ/bn03xOx/va9QALxvgCQPcaYB3zVUZneA9+gbYMkev+TcRr/fmOfg93sGymdZrbzPAPwPYC+BbAP4SwHUANgBoBpBCmMgmCaAJQ -C+AnQDeBuCvS485ZID7DHClYdKbSA9ghyS7M50NzXVX1+dz3ZaITA4+vFJbV9nOhcfcwEiXsuo69x3ZkCh4K2xttcViMU45JgYgRKfiiPZb8q31V2SSyTWOUmoi8Ai/ -UB75KHiTvpdiEZbvo8EL0r0Hh1flj4ys0Vp32radTqVSC/6aF2APgEcjTv3aLL7MLtQGGy6A82bp+S+OOPYrAeYqXdlbI445AG6e5x+3C6AFwEoA1wP4WwCPG+A7Brj -yNLcVH2OP9qu/bEqBx7cAfBfAWwBkT+HpkgBeC+AeAM/NVgfGq8n+WDGZas6dn02nL4jbtlMztXhSOzcRlKDoQXkeMgGcvsGxvqYDQ+ssoNe27bpUKsW/U2IAQjQTd7 -ibnNzixm11dblLk46TnJj/WgQKE4FGedSjPCVrvLIuFpEyUF1Dxbaug0NrY0V/mW3ZzbFYzL366qtfDb1DX57jAOSyKY5fMkvPf1HEsa/O0Q3VZgCrpxGYLATnALjHA -J87jaM4qwG8m7XTq7NsDJA2wMcAfAfhSMZsay8F/VRyGzao7IqG3lxd9oZcOtmkfF/K7ZwUJtqyiXauckQkPGd7viwqIte372h/erS4ylKq3XXd5E033cT7O2IAQjQd -d2KTxDJud10ufVVdKtltm0BQ2RMUse5j0nSs0nnteWj0Tax7/9DyuiPD/Vqk27btXDqdtl6lAUinAZbP0vNfOlcBSGkqxq6IU1+Zo7I6XpDRb4CNC/g6uAbAAwaoO02 -v92cGWMZa6tVVNgZYA+ARhNOnpuqg8RGOvN4O4K8A/AmA/wbg7wD8C4D7AezjZXDy4nWxbCLmXtmYz66Lidi17VyxanS/atpxaRQkHQRWz5GxjpZ9g+ssg8WWZTUkEg -nn9a9/Padi0bxlsQhovtFZK1XXW39RNpW4MK60LQU/XPvh+xBv4v+wFCQeA46NAmMFwPMgXjBx3vcR8wNpHyw29u45vO5o3H3Wj9t7EonE0E033eTfcccdwQIupocRz -s1uqjr+awB+doo3I30AlkxxeqUB2gV48RReYiOAXNUxD8C/zcGNVRzADSf4sbeVbr7OhC8CeHCKc3apnDoQjuL0TfFzaxHOsz9XgLm+pl0A/2SAXQJw07NXQdmYcLTj -PgCZKX7kGwjXgtwnwJGTeL5eANsB3ALgQrCjM9Jns9vsVFt6fb4uc3XacTKq6AN+Od2uDym3YwCQdMPAY3Co1PHmQ/yJts7yfDQWvdTSVwZXH8zEn9mbS70Ui8UGc7n -cgVLgSDTvsGKgeeUubFKptnRvLpu+uj6ZrNeeL/CK471B4+s+HAtqaz/0G18HdclWSC4xaQRkfP6s56HOD+yePYOLm48Mr7ENemzbzsfj8QUdfJducKJGDGZjGtblJz -h/qqMgUdOvvncyNzczcA1q57B/ser7G0uBypnw7wL8zRRffyXAuwW4SYDFCBf/fm6K5zkHwG/O0XssVn2/cw5fa6FZ0GVjgPMRjlxEBR+PAjhHgAsFuP1k/z4FeFaAT -0lYT/QgXJA+yktlwp3YJPGGeD6VTV7bnMv02YHRkzM7lto6YyDLOqGvvwzq6osgnY0Qz68Z7Zeih6Tvq66Dw62d+4+sdb1giWVZTfF43L3xxhs5CkIMQIhOxGlw8ul0 -/OqGdHq9A7FQ9MazfoRrP4qQIIBa1Qt96flQ526DvuxiyPYBQEvkwj276En70bFM90sH12WGC6strTtc103efPPNC/36j5qGdZ4Je2JPxWVnIAC5f47K6G1V3/8CYY9 -upSxmMYPYHAadj0qYheg3EN3D/udzlGnojohj7zdAK2ushVs2Jly3cg9qg2+DMOPVZgG+d4rX7AsCvAfACgB3g6NmAIBYnePYjnVRS33uwriyklI55aq8xtHzIM156M -vOhz7vHOiLzoO6dBeQdmvbuaIHVfRQP1KI9b08uLzx0NA6S1SPbdu5WCzGmS7EAIToeO7CZjvZkBzIpZPXZB07O556sDg5HSHqUlCbB6B6uiGJOKS1GWrTOkhf66QKe -Twtb9FDulC0el480N128MhaNzBLbNtuTCQS7vXXX7+Qe4e+hnDqUqUEgHNP4aYkjtpMVw9VBxBmhnVHKQvOtohTs77+ozQV5LyIQOcbJxGozOdA5GMA/jziVCNmL0tZ -pQcA/DwiaPsH1loLs2xMmEL3XtSODvoAbhHgz2QWp+4I8JwA1wgweLZfMHdis6i41VVXn3lDzo11Wb4vUWs/YCmojaugVi2HpJKQfA6qfxVk48pwetZ4J9vEWpFY0Zd -Few439ew91J8cK66wLKs1FovFuQcWMQAhmsK92CKp9kRTOh2/sTWX6baCQNVm/SgCYiCrF0PWrADsMNOg0RqyfClkU39F71DFdK1iEbpYxKKh0Vjv8/v6c0Ojq22lOl -3XTTuOs2D/BkqN+bcjTp3KNKwLAMSqjlWvUahHOB1oJs5F7QjNHgA/noMiegtqF9TeJcB/ojbd73mlgGWh+OtSuVV77Ry8lgPgnRHHrzLAVWd51bVQy+aDiF5T9GYJF -5nTHEnWO8lUPn5Fa112U1yJU9nRVt7lHL4P6WiC2rIeSCUBKVVjrS1Q2zYC7XUV2bEqRkG8IurHirr3VwcWtxw8OuAEWOw4Tr1t29YVV1zBqVjEAISommeLY7v6wqZc -+kIXkhDPn5T5A4VSBd1aB2xeh4Mw+M8nHsdDDz2Exx57DHuODcHftA6yphdigonpWuXgpeAhNjKme3cfaG3ffWCD65ullmW1JBKJ2AJPyzvb6Xirp1+9hOg9R2Y6DSt -q+tXXZnvRbmmE5taqw79EmGIUAD5VG88tnJS8Es6pvy/iVNccvJwr4XV2V8S5vzentk/EQrfgysaEC8TfEnHqfQLcxtZo7nzW2ipGZG1TXeaKpLaalRdMbufKnW6OBT -l3E4ZyWTz9zDP4wQ9+gB8++iie3/MyRns6ITs3QiypypYVtpN2oShtrxzO9by4b21qdGyl1ro9Fosl0+k0AxBiAEJU6U57m8Ra4t0N9enr8rFYm+UHUt50qXLjJYlZC -NatwEvJGL7w5S/jfe97H379138d733ve/GZz38eT48Oo7h+NdCSqanQpehBFz00HBxyl77wyrLGw0MDFtDtOE42l8st5DmyUQHIGgO0zfD5qtPvfge1U7BmOwCZi/S7 -FyPMHlXpjopA5w7UBj23moVVJz4WcaxlDl6nvK7k9wAcqDrXBuD9Z3H1tRDL5m8jjv0QwP/D1miOOw6Suq6xPXt5XSK+LgboyWs5Su1cEED6F+NwRyu++fAP8KEPfQh -vf/vb8Qd/8Af4549/HD984XkcW9wDrOkKMz9WpaeXoofssVHd+9wrXR37BwdcL1hqWVZjKpVyr7nmGgYhxACEqCye1248bl3enE1tjAEVQ9LlzQe9cM5reyPG1qzANx -/+Af7Xhz+Mz33uc3j88cfxpS99CR/84Adx97/+Kw50d8Ks6IFYZlLwUf6/O1aUrr2H810v7V+XHPOX29pqi8Vi8RtvvHFB/i0I8CTCnv1TDhBKe4j0VB3+pgDPAthdd -XybAdLTfP4GhCljKwUI17LMtqg1HbdVlNuvAHyz6nw7Znczx7l2MOLYXGz0pkplthfR041+05zCuqNXQxu6UMrGhGuEtkSc+m2pXU+GefS+dxjghwYYM8DjZm6mGs6p -z8e2qGTO3tSQSV6ase20FKuyWZWnU2XiCLYM4Ce7X8LHP/EJfOxjH8OPfvQjPPjgg/jQhz6Ef7ntNjxra/gDqyBZp6qdK0859tB2YCje/fy+Nfljo6tsCaccJ5NJ3vM -RAxCisFLepizBpuZ88sqU1o3a82tS6cIrApkY5OIdGG5uxA9+/GM89dRT8LywvfR9H88++yy++/DDeN734L/mAmBJC8QPp16hUAw3LAwCCIDM4YKz+MX9vS17D22wgD -7btuuTyeSrbRRkJjfSUdmvytmpqoMEG2EKz+m4ELVrMh6W6BvpU7lZqQPwuqrDj0rt/iifinj4QtoZPWrzwQNzE+eO/+dTqM1YJgj3v3DPwipsoZXN70Yc+6IAP5jHw -UdXqVzXlwLsVQDuXWgbiGpHdTfkk9dkHXuZ5QcyqZOtvGZRDOT8TfCWLsZTu1/CQw89hLGxMRhjEAQBDh8+jH//1rfw1IEDGNm0DtjeD0Ew0c6V1o8AQKwQqJ7dB1s6 -Xtq/Ieb5y8pTjq+77jqOghADECI4ksvVJS+vj8fWOga6ZmfzQjEMHFb2QAbW4JgSHB4cxLFjx2qeaqxQwBAM0L8asmMjkLHDwMP3w3520YCyYEFJ28tD2cV7Dq5KjRZ -Xaq0Xua6bvPXWWxfq30NUAHLxDKYTVQcgT8nE6EpUmtzpjrKcrvS7N6P2hi9qbvvnAYxUHXtdaaRmIVgcceyZ0/C6/xeAoapjywH8GSu0+Vs2pcxXUXv8fGiel+nvAE -hW388D+KOFclF8WrbEsk2JrflE7JKU1jGp2sNjfM1iZyNk4wBGs2kMHj2KPXtq80x4noejY6Pwe3sg52wG+urDQMbzgSAIb+tEQ5RGw6Gx2JJf7V/aMDSy2oJ0O46TT -afTTMtLDEDo7HavtdV2E2p7cyZ+SVrrtPLKKQVLw9KlkQupT0HtOgfS3ATLcaBU9GUrIlBaA/V1kAvOA1Z1la5wBSgNU/oSpZEqQnftOdLe9srhdY4fpuV1XdddoAvS -vwlguOrYtDJVlW5Ozj1OcPB11KblnG4AcmHEsblY/1G9wNYH8Oma6yXcWO0L1SExwh2cF4LLp7gW5pQALwD4k4hT7y7tLXHWmudlcylqM9y9jDCV8HzWM8XxBZG17l5 -sUYm87ssl3WsbYm6rnpR2t9TOFYsQR0O2b4Ba3AsrFoNlTR0nKKUgyQRk22Zg0xogU/pZKbVzOuxsc4ySjgMj+fYXD65PFLzlltZtsVgsfv311/PejxiA0NnLyqrOlr -rk1RnLWmEHgVQvxkPRhxgD2bEesmYVVCqJVCqFVCoFu5SCt5Jt24jH45B4HFi+DLjsYqApNR54oPRltAWtNOr3j6WXvnRgdf3gsX6tdJfjOOl0Oq0XWjmWMiJF3URMZ -xrWhahdP/CVitc4CODhqvNLDNB9kgFOX8SNxIGI5zwlJpymMVB1+AGJTlkLRI+MzPtpWAZ4PYAlVYcHAXz1NL2FfwDw3eo/QQAfNWxX5mvZ7Iw49mWZ/5sD/mKK408v -iJusvMrU18Uvr3OdnTFjrOoNBMNgxIf090JtWQ9pqEc8kUAqlUImU7tBvdYa8Xgc2raBjnbg4guBFZ2AsmC0BajwyygN0RqpY4Gz9MWDi1v3Da63IH22bdenUimbdyD -EAITOSrfrDbF0xjm3Pu5clFbKnUi1W5EP3SsCvc2QnVshLU2ACLTWaGlpQVNTU81zJhIJ1NfXQ5dHQbZvA7avBVwb0BbMeAUd9hAljaiOl462de4/stb1/GW2bbfE43 -H3jW9840IcBTnVdSDV069GAHyr6tj9p/AaUdOvvi7h5LjZ9LaTDDLKvgpgX9Wx1QbYPI+Dj0YAfxdx6sOlUZ3TEfQGAH4dQKHq1FYA/+VsrtvmcdlELT7/7gIo0r8Hc -LTqmIcFkH3tNlmv40lrWS7uXFfn2DlV9GWinStO7PtRl4Bs3wRZugTQGiKCXC6Hnp7awR/HcdDY2Ag3FgNiccjGDcCOzTDNyfG2rdzhZpSGrZS0HhjL9uw+2J8aLa7S -KkzLy80JiQEInXW+gnNUNuUuzbvO1XnbbtF+MF4pj/cOeR4Qs6Au2Abp6x3fdNC2bfT29qKhYfI0fdd1kc/nkclkwilalgX0dAOXXQTT1xAOSWs9aTRElIX6UXF6Xjy -8vOng0NrSHNmc67oLcY7sv0Yc22qAzEk+vjr97jdLIysnCkBOdhrWnKffNeH0khurDg8DuOc4N4seIqZnYZ6OgpgwtfD9qB1NegbAX57mG+0nAfxFxKn3mdoUyGdbED -Ify2ZFxLEfLYCyfLFUP/2wFNQ9AeD1Ep2Gej61c5JLxeoTlvWGppi7zAmMRvXaD68IIIBsWgnZOAAk4uGNmVJoaWlBV9fkbX1EBPX19chms+EsACVAQx3wmouA/j4Y1 -64Y6Q/bPFEaSV/pnleG29tfPrjeCcIpx/F43OWCdGIAQmeVo9rL5jLOpXnX3hmHsSbnQi+vAylCNi6DbNkAqZ9I9mPbNnp6epDL5SY9Z1NTE3p6esIpWOVdY3NZYNMG -yEVbAdeePA2r9K+rtLS9MtbY/crhgcRoYZUuzZF94xvfuKD+NkppZX9SddhC9LqL6pvaNai9KYoKNh5GbZalC8zEXghTPb9CbcYsg9mfLnQVgHzVsXuldlFwtahsWDc -aID6PAo+UAd6F8KZrfdXpIwBuOInfcy78NcKd5SulAHyYNd38KZtSYoVUxKnnFkj99h0BNkq48eNqie5wmW/tnO3E1bbGhPO6lJKUeFUbDhZKI/3djZBzt0I6O4DS+k -YRQVtbG9raJm/n5DgOli9fPtHRBgCuC1m5HHjNrnDKsa4Y6VcaRlmwlIWGw0Fq8e7BVfkjx/q1Ul2O46RTqRTvAYkBCJ0dbpeNuq7BWVYXt67NWypV3qV8Yi5sKSVhe -Uh6SV84mlG+o7YstLW1IZvNTgQaABobG7F69Wq4bkXyI6UgXZ3A9i0wy5onKmQ9sRYE2kIOlu5+aWhx6+DwGtuEaXnj8fhCHAWZ6TSsSyOO3R9xExAgXIxeKYcTT1da -h3BRfKUfC/DKLP/+UaMWJ9zZWYBHUJuiNwPg2tPwmZ1vgP86xdefGuDvTLi+Zy/CDeSqA6x9AC4u/Q5n4sawiHDaW3WCgssNcMPZXNfNs7Jpjjg2JsBhtkpz0s5JKmc -11iXtGxpce5H2fBnf56OirYMjkPUrIRsGgNhEfgARQWNjIxoaGiYtRo/H41izZg2y2WzFhSZAvg6yfSsw0Ac45eBjYi2I0RpxbUn7vkJL176ja10vWKq1bnFdN3bzzT -dzFIQYgNCrX9LVDQmt3tjo2iucwOhJO8CWK2cEkHMHIJvWA6nJnXZKKaTTaSxduhT19fXjlXU2m0V3dzccp2oddTwOWbcWuGgrTMqtWQsCpaGVJW1Hke564dC6zEhhl -Va6w3Xd1C233LLQFqTPNACpXv/xrEy9wHMm60AuPsnnmTEDdAK4oOrw3oiAaSpRgcrbTsNn9joA/2OKr78A8I7S7xU1GnMvgDVneg8HCUfG/v+IUx8w0XuVnE1ByHwp -m2TEseFT+HvbaAAzg6/9Z0U7Z+uYDVzZHLPPTRjElVe7Wzl8H1i2CHLhTkhDfRhIVAQg8XgcHR0dWLp06cTzJpPo6uqqXZxul6YcX34hTHM6XAdSXguiNaAtiLZQV1B -uz0tDK5oOHh2wRPU4jpNzHIdpeYkBCL263edsdfIpa2tD3Lo0BSTGAw7Pm9iIqehBlrRAdpSGpKW2c8ZxHCxevHh8GlYsFkN7ezvq6urCBeiT7gAE0tYGtWMrsKFnok -KuWKwnSiMhtu7aW+hYtH9owPWD8hxZ56qrrlpIvUPfR+2mft0GWHqcG4kMgO3TCA6+itqsOSdaBzLn6z8Qpt6trs/unMbuzrdF/F47S9m75pMxhGtatgpw1RyMIs3Uf -wPwbNWxJoSjNme7+VA2URshevxo5qSdk0RML21OOa/PaNWs/cp2rqLDLetCnbcVsmrF+BrHSlprtLe3j0/DEhF0d3ejsbERsVisNtjNZiCb1kMuWA9jlzrYdDgSYpQC -lIKrbWk9FNT37D3WX9oDqy0WiyVuuukm3gsSAxB6dboDm0S7aE066o3NjrVIe2E2ECl9jU+/iilgcz9kw7pJQ9KTWlLXRVdXF1Kl0ZFcLodly5ZNXv8xKWKxIQNrIOe -sA+oTEWl5NbS20OI78e4XB/tzI4VVlqgu13WzmUxmwYyCSDjVI2pdxfFGKC5BuFbkpIKDUjrb6rUmmw2QnSLAiUUEOIMAvjdbv7cJd5t+yxRBxcmW3fMAHqw9PK8Wo/ -8MwAoBrhbgoXl27Q0D+I2IU282J7EO6dVsnpRNIeJYjC3T7CuKScdjcmWza62PBYEVrvUot3OlERDjAyu7gJ3bgWw2sqNNKYVFixaNJ1xRSmHFihXI5/PR7ZzWkO4uy -K5zgO66cC2IssJ1JeUpWdpCXrm68+WRJc2Dw/22CdPyJhIJpuUlBiD06pRznLir5XVNrj7HDXxXvCLEq5oP63tAfzfkgp1AU2NkpQyEC9Gbm5uRTqchIkin01i8eDHi -8SnWDItA8nnIeduADb3hcLWevGGTURYcZavOI7qp88VDm+Kev8zSujkej7s33njjQhoFme40rOr1H2MA/v0Er1E9QqIx9Y3UjogbnQdkdjyWX54AACAASURBVHtfLwT -QVXXsKZn+HiNRi9FvPdEi+9NoOYCHTe2Uuflyo/0NAP874tQ/zqcF/Wdp2YxEHEvOo2v7VeEBfb52LWxrcvXlSZi88krp5Cft+1EEWlKQyy6CdHdNWuNYHYA0NDQgl8 -vBsixordHb2zt1AAJAEnHIwGrIJZuBuA0oVRrpn2jztLKkZdRO97x4ZCB3bHR1acox0/ISAxB69fkstinRQV9TTF2ZV2i2PF/Gh6K9irUfDXHIjs2Q/tWRQ9JllmWhr -q4OK1euRCqVQiKRwKJFiyYvQK99ENTypZDt64GWdGkEpLQeRJXTFVrIB47bu7e4pO7IaL8lqttxnFwikVhIc2TvR+3eGrtM7SaDZa+p+v5BAY5NMwA5XpBzOqZfRY1S -3D6jS7U29fAiTG8/lel6p4Shtkg44qIRLtg/D8AHEb3D/b3m9CyQn4n/inB37Up9AN7LmvCMls2+Kdr/lhk+368A/OFJfJ1VjmC0sSmhL6uzZLUb+GryovNSW6cN5Jw -ByJaNQCY9ddBa6lzr6+tDZ2cnLMtCa2tr5OaEFQ+Cam+DbNsArGyd2JhwfG+QcBQkpV3dud90th4a6XeDYIlt2w3xeNx9wxvewAXpxACEXj3EKmaTjrmm2VZrXD+wJm -02WN6UCQFkVS9k53YgnZ5y9KMsnU5j/fr1yOfz4/uC2McJWiCAJJPQO7YAa3sA1xnPhlU5PO1oR9oKbr5r95H1qdHiSkvpNtd1F8wcWQnT5H6/6nAS4UjEJCbMTtV2E -sFFte+gdmOwi6cRgMzaAnQTZuG6ajYCEAmnht0Xceptp/HzCwQ4KMB/CPD7APpRu9+BDeAOE72x3Jm+/g4jerO9d5Wut7O3HjyzZfMKoqdhLZnh7/KKAH9zoq+z6fO9 -XTa4qXiws8GWi9PGxCbSypfXOJY63DrqIBecB7S2jKfdnYrjOFi9ejXa29uxcuVKNDU1IZFIHP+N2Db0mpWQHeuATLxiyrFVse7RQpPEY127j/XXHR1dY0G6HMfJxON -x3hMSAxB6dbjb2qSTlmxpceWKJIIGVRrxkMrdYIsepCMDXH4xsLjvuKMfZbFYDG1tbUin01i3bh0ymcyUw9LjLA3p6YS6ZDtMW6YiLW+5h0gBWiMFx+k9iN6W/UPrLY -RzZJPJpH3NNdcslN6hk52GFTWV54SjE6X0ot+oOtxjqm5mSll+qm+snihtLDZbbkL0XPZnZpKlB8B1Ec91RWn38TNx0/oMgJ0AfhwRhNxlatPyzocb7XsAfL76rw/AR -8/2KT9nqmxKKbSfiTg1wFZqdqRt1dPoyrU5wRLL92V87Uc5+Ch6EFeA154PDPQDyeQJn1NrjdbWVmSzWfT396O1tXVi/48pP2yBNDVA7dwEDHROZH4sr3ssbVDoaEd1 -HrVbOvYMrY97wTKtdUs8Ho/dcMMNHAUhBiC0sH1BtooYNDe58ro6ZZY7fmnqVaE4OfVuTIBz1kO2balJuzsV13XR2NiIVCqFzs7OE/cKlevmeAx641rI1uUwiXJaXlX -KEhJW0Ja20VR00737Cquyw2OrLaXaHcdJLqDeoZMNQKrXf7wgwE9P8jVOZlf0CyLqmPtn+Xc9HaMTNoBbzuBN6xEAr0VtCtMuRKd4nQ9+B8ChqmMbALyTNeMZK5sfRh -zbxY/j1H1WNqUaXOyq12Zn0gR6fD+rinZOPA/YtCQc5W9tOeEofzkAaWhoQDqdxqJFi8ZTz5/EA6GXL4Xa3g80p8IOtsrUvEpDKY06FXN69vtLGo6MrnUgPY7j5BfoH -ljEAISoog4E3JRldjVY5sK0CRKqas8PKYRD0uiqAy46P6yU9cl1AmqtkclksGzZMuRyudr9P6a84hWksR76/C2lTCETaXmhdNh7BCAptm4/Iu1t+45ucH2z1LbtpkQi -4Vx33XXzvndIwik71aMM/aZivnep53zrKQQHJ7MOZE7Xf5hwetL601Ssbz3Dn+lLUwRbb5qPWaZK2dL+IOLU/2uAnrO5XjyDZfPtiGMXGGbDOiX3YZtOW1iRUbi2Tky -D9jyZtO6jUAw73XI2cP52YOlS4CTbq/J+IL29vchms0gkEice6Q8fCEknobatB9Z2wTj2RBun1HjwY4uSRWNuvuPloxuSBW+lpXVbPB5P3HDDDbw3JAYgtDB9HpvFiN -/XaOPKDIJuy6vahKmUFx1pC7j0fMjAGuAkRzHKXNdFa2vr9AIQlEZBVi2F2rUWSDiAaEAm/yloEdQVrWTPPm953ZGRfh2m5c2kUqmF8jfzr7X3PZNGKH4NtVM+TjoAE -eA5AE9VHd5lwtGCqQKQY6hNdXsq3nYay3PVmV5zIcAXES6Ur/ZhE73Pw5m+0f44gH+rOpwA8I9ne/14hsomamQ0DeBqtlgzFygvn9HmdfXKbHB9X1dOLR5v50wAXLgJ -2LYVaKif1vNrrdHc3IxsNovkSUzbqnggdHcn9K4NkNZMqYNNlZqC8nUoSELb3YfQ3bx/aMA26LNtuz6dTnMUhBiA0MLkIUjW2/75Dco/LxX4zvh0q/L0K88Pf3CgD7h -gF9DWdsIFedXi8ThaW1sRi8VqNyA8busvkMY66K39kJ56QEX3KMWVrTqOqNauA6Nr40V/udZWi+u68VtuuWUhzJE90TSs6vUfRQAPTPM17o+4mdkGACacHlS9id+/S/ -RC2GkrZfV6Y8Sp/wHgd2fh69kzHPBM5R0AhqqOLQHwnnl6Hf4majN5XWyAN03+OM9Kp7VsSmuvvjfFNUUzcCc22mkd9DdYwVV5+BmpSLBSHu2HHwBLG4Hztk+56eDxW -JaFlpYWJBIJWNb04gJJxqE3roGsagec6DbSEi2tBSfTt3esPz1cWK2V6nAcJ33rrbcyRTPNKUa5NOu+iK1aqeKqjJir8wjqVdGH+EHYExQE4XJfCNCZA157CWTZyQ9J -V3Jdd3wK1kkNS1dWzLYN3dMB/4J++C99Gzhae1+sRJA3rtO+d3jZS5ljawuN6ecDxzlQLBbHSjfs89kDCPf0cKtubiQiGAGA75bWGkw3APn9qmOXAPgPRGfFms30u69 -HmJK20h4A7yltyHiqAU4CwPurDl9vgHdI7U3jaSPAbgP8fxHv7Y8NcIcAT8+ni1CAXxrgzwD8z6pT/9MAX5EwPayPs9AZKpuPlDsJKmwywDVSuziejt/OiYjXHIO5oV -H8btsLlPh+2LkWBOEXBMjHgAu2AVs3A8nUtF9Ha41FixbB9/0TL0CvacQUVGM99AUbYX7xCszzgzUhrQBIiKXbB71FL7xyZP2xrvpfBrb9SiwWG77hhhtG77zzTsNPm -+YCR0Bo9htW8fONylzRAH+T63l6fM8P3w9zsYgOF3+fux7YtgVobDypBXnVstkszj33XHR3d8/s4q/Lw9q0BmqgvXJUehJbaWk9ZtX37BtdmxrzVmptLXJdN3HrrbfO -67+d0l4e36w63IiJdRNNEcHEdH0LtftmlAOPC6YIWGZL1GjEnTJ7N2yfRO1miRlEZ8k63f4OtdPfXAD/a55ejh8A8IOqY/WYWEA/ehZXl6e7bO5CuIdHtQ+WUlrTybd -zTlqZXa3KvyTh+4mJKVd+OOoBBYiG6e8Gdp0L6ekB9PSbDdd1MTAwgA0bNkw/AEE45djqXwa1bRngRg9qaFGo86zk4v3eivojI/1a6S7HcbLJZJKjIMQAhBaGO7DRTo -g/UCfe6/KmmFCl3Odhb5CaSH27pAGy8xzI0iXhzuQzoLVGMpmc1vqPSSwN3dsJtW0lkItNdSOPvHZ1536/t/nQyFonMH22bdfbtm1feeWV830qVtQ0rG2YpcXhEu6u/ -K2qw+tKOzufV3X85xI9rWnaDNCO6BGWT81iALcHtetogDO8GL303oqoHXkCgAsNcPM8DIaDUsBYPWp4kwk3wjyGs9TpLhsJR0X/W8SpNoR7y/Ce4CR8GhuVgt+VE++6 -RhRbbK8o451sQNjJpjRMUxLYvA7YtAGIz2ytf3khejwen/ZIf+kJIA110DsGIB3ZKTvbEtqW9iGruWP/yEC84C23LKvZtm2m5SUGIDT/fQXnSEbQkoL/xkZTXGx5noL -vh0O+5QpZacC1gV2bgS0bgWxmZjehxsD3fYyOjqJYLMKYmY0SSyoJa91KqM3dU64FUaKkxXPTnbuHBrIjY6stpTtc101lMpn5/vcTFYCsB7C96tjLUrvZ3cmqHtWwEa -aMnckGhyfrzRF115MCPDrL5ffPEcd2mhlu3jbLN5JfBXBvxKm/nad7gzwO4K8iTv0Dzu4RkDNRNp+I6DgAwrTcH2cQcmIZqEQc5qoWFLe4vueGnWylWyoJ95YylgX09 -0Au2gVpbpr2GseyIAhQKBQwNjaGIAhmdo25DvTibqgL+qccBREIcsZ2u/YWlzYeGhqwIb2O4+SSySSn6hMDEJrfRlCMpeDvbEHxwmRQjElQMQxdsdu42dgNnL8D0tsz -o0rZ933s3bsX3/ve9/CFL3wB999/P5544gkMD89gar4SqK5F0DvWQrqzU90gICm27h5U7W0Hhgdc319q23ZjIpFwrr/++nnbO1QacfhZ1eFOhHsOzFZwEPXYqyLj01l -QWsPy5ohTn5qDIvwywh2kq711nnzE70Q4ClWpCcBfz9NL8i9Ru89ML6KTCZxtTlvZSNgl9GaEO7NXexOA+zgda2p3Y5NS8Ne0ofjaTFBs1IEflqpS4YZ/2go727pywE -XnAmv7Z7TG0RiDwcFBPPbYY/jSl76E++67D4888ggOHDgwow43VZeDtbUfsr59ys42R1nSNmzVd+8fW5MYKay0tG5bCFOOiQEIncU+i80qgaA3b7wbG0yxSRnI+HQrp -YHSTqymIQHZth6yaQMQm/6QdBAE2LNnD+6++2584AMfwDvf+U78+Z//OT760Y/ioYcemlEQIjEX1sAKyLqeKXuHlAgaEIv17B5dXXd0bI0F6bZtOxePx+f7HNnqUZD1 -mMXRCQkDnOeqDldvcDiK2vUoM3UearNrGQC3z8GNmodwLUjNTdp82NG7lAr5/RGnfsPULjSeDwFxAeF0o+pu3C04y53usildO9cgOpnGZQAeN8B1ZsoJOyfVWbBytt6 -vAbYa4GEDjBngP030xqqnhYZpqIN3dSOKq11jFKAqOthKI/1xG+hfAtl1LiSfm/YaR2MMjh49igceeAAf+chH8J73vAd/+qd/ig9+8IP4+te/jv3790//jVsauqcTek -c/kHGmui6Q0a7u2h/0tR0aGXB8s8S27QbHcWwQMQCh+ciCSdowVzeLt9kRuKImdlwNdxvXgGMBA73hgrzm5hmNfoyOjuL73/8+br/9dtxzzz14+eWX8cgjj4x/v2/fv -ukPU4tAmuqht6+FLK6fssl1oVXHMbupbfeRDXHPX2ZpqyUWi8Xm8yhIRADSWPW9D+Drp/gaX636vrr39D+ktqd+pqIWn/+HAC/MUflFTcNqiwiyzpT3A/hlxH3ER8w8 -zHIowPcB/D1rzDNfNgJ8A+EIS1QQsgjAZwD8xAC/bWqTVkwVKGRNmC3uSwinls1G8LGoVMdsRJh+ezXCUZr+0/0Z3Y0tlgWzs0H8S5NisqJUacTDmljfqC2gqw7ymvP -DUX5r+vfuQRDgpz/9Ke655x586lOfwtNPP42nn34ad911F2677TY888wzKBann4hR0knogeVQW3sBHd1sWaKkueCkO3YfW5seHltlKdXpOE7qlltu4YJ0YgBC88tnsE -knYdZ2SuGyjDL1Sulwh3FljY98QGuY1jRw7ibI+rWAO7OF42NjY/jOd76Dxx9/HL4/kfDowIED+NnPfoYXXngBnudNv2K2bVhrV0BtWQqko9+bEkEOjrP0EBY3Hh4Zs -IFex3Hy83zTpm/j+Ol1HxLg0Cm+xolGUGZr+lUGYa9ttU/NVeGVRni+G3FqXkzDknB06V0Rp/oxf/d3+BMAz7PmPPNlI+HGllcep45YjXAtyh4D/NQAnzbA+wzwhwb4 -PROmf36fCRewPwZgP4A7AVyOUxg9qfI7CP/2K9kA/vh0fjCfxHqxYDo7pHh5nfiLbaXElAIPVLZ5CRs4px+yfQuQy86oFIIgwJNPPokHH3wQIyMTfTee5+GJJ57As88 -+O8Mpxwq6ux16ez+kPT3ljyWUrXuP6EWdB0fWxbxgqW3bTYlEwr355pu5IJ0YgND88GlsEgtoTIp/db0yyx2lVGXgYXSpVyhmAwOLoXbtgGQzM0q7W1YoFFAo1O7b4f -v+jHqFwpZYoNIpWOesgyxtnLJ3yFZKWgp2rmPPsYHkqLfC0nqR67qJm266aV7+LZUyJn1tjoODB3D8fVFmawH6jQgzbFUaBfC5OS7G/x1x7LUn2yt8Gj7je1E7CgUA7 -zXhmp/5dk0eQ7gJH82DspEw29tGHD+JgwBYAeAGhJte/neEKYT/qvT9jaWg93idMd4M/1anSvqw9HSWkwOdSIl3fk6Z81JanPHAozz9SmkYS8MsboJcvAsyg811JxWW -500KPiqDk0KhMPPEKzEXes0yyKbFx0nLK6iHG2vfM7oqf3RsjQ3pdhwnY1kWR0GIAQjNDynLWBkJzmtVwWviWrJSnnKlS6MepdEPtGYgF+8Eli6e9k6wkwIA20Z/fz9 -6e3trjufzedTX188oV3r4JBb0ki7oc/uB7FRzZAXJwHJ6D6J70b6jGxyDxZZlNSQSCfvqq6+er71DX57L4ECAo4geJQCA56R2IfxMRY063CfA4ByX32dQu/u4DeCWef -QZ/x5qd5lPAvjQPL3R/hrCbEw0D8pGgJ8D2IwwvfOBWX76MYQbIC4W4Ldm8Phnpjj+i9NVPvc4GyUrZlmjCq7LKdOhlBaMLzgP134YrYGYDVy0FTKwBkglZ/55iKCrq -wsbNmyoOZ7P59HY2Djz9PMiUK1NsM7ph/Tmjxdwqc4hu6nz5aMbEp6/3LKs1gUw5ZgYgNDZYsw37Y3Kv6xOmz5baTGl9R7ji8/Li/Ias5BN6yDp9Cm9XiwWw44dO3Dx -xRejvb0dyWQSuVwOO3bswOWXX46Ojg5oPfNOGkkmoDeuguTjU8cpSqO56GS6DxRXpobHVmulOlzXTaVSqfn69/QV1Ox/CyDcafmHs/QaUwUyX52NJzfAqtINUrXbTsP -N2VApCKn2tnl00/o0wg0Kq73OhFNs5qN3ITrLGJ2BshHAF+CDCEfNfhenltZ6DMB9AG4F0CLA22XmU8v+AbX7ofgIR2FOi5EisnnlX9Cggi1xrSwzPuVqYpQfyoJxbc -iGtZDW1lMa5ddaY82aNbjkkkuwdu1aJJNJpFIprF69Gtdccw1WrFiBWCw288/atqBXLYH0TD2Iq0SQF8fpORj01R0Z6bcMehzHyadSKablpdmqc4hm2CuETamsCm5Zr -P0/brbQqZUCRMEoFVa+okv/V8DqZuj//m6otaunGAE52UvRYGR0FE/+9Ek89tiPse+VvYgnE+jrW4wNGzagsbEB6hQCEHgevCd/gcJ7Pw7zy6k71n1j8LIeOfZQh3z3 -6Y66zx2z5MHh4eHnBwcHR+666y7Dq4OIFjoTZpzbBWArgGUAuhEmmUggHHEbLH0dAvAkgB8jXAvyQ6kdNTyV93Euwt3hVyMc+Xi3hAvd59znsdlOib+jQwd/3W2ZDY4 -SDaVgRIVTrKTi/ykH6v2/DXXZxZBk8pRuuzzfw3O/fA4/+tGjeP6556CURnt7O9ZvWI+urm7Ytj3zO7jAIHhlH8Y+dAeC+392nHI3GDSF4iMNhSd/srj+c4fj9tdHC2 -M/O3To0JFPf/rTAf9C6FQwkqUZuR3rxZZgWbM2V+Ys1a61TKqIJ1XOSgF7j8H/4tcRvPgyJJGEESn1EEmpEpWKY6g4V/l9+K8FYEXRQ3uqGcN+DHYshrROIfbzX8F/5 -iX4gsm9T+OPraz/pbY9MACOjcB/5AmY/cdP2qRFUFe0E717x5bvz470jzWkXnBd91AikSggnOtMRLSgSTj96RlEr4M6ne/jQdTuX3R6bpLEr88pXN2oscbWaiL4mNTW -6fDfoiB44HswxQBSXxeer2jjIKV2blIbN3Fuclsl6AgC1Lt5HKkLIFohlahD8uXDwN6foFjTNqLi+U7QzhU9BE89B/Ozl09Q7oKkse3eQ8WuvfuH1h3ryD/r2/a+VCo -1cuWVVxbuvfdedrYRAxA6vRRQV6fNa+q0bIxpUWbS6EdV8CEaOOwjuO1B4IuPAHG3tD5ElTKIqEn/h0hpSFsqnqtcQU9UuCkBkpDwWyXwyuekshKueIxEVNI1AUgB5l -eDwJh/wjKIa0sWHfOaug6OrT2cjj3rO9Yrrusee8Mb3nDsM5/5DHuHiIgWsE9jQyypzPYGS34tbWkXWsaDD6N0VRCiAKMQfOUJ4Nu/AFIxwLIm/1xlO6dq28uwzcOkY -MQVoBETx30F+CfVzqGqI66inSv4MLsHgcHCCcvAVgpNnpvqPVBYtb+u8LSXcne7rjuYTqcPIZwKR8QAhE6PO7HRyiqsy2pcl7ElJ1JdmepJPUTjIyOBwBwpAEf9qlES -PfF/JVWPFZxonPlMdcEIwrS8HfuGl76UHekfbUw/5zjO/lQqNYraDcWIiGjBBB8bJaWwKK3klnobnUopVd1e1XS4lduuYz7MyEhNoDJploCaXjt3JsVF6/YjsuhXe4+ -uOxp3nvUta08ikRi+7rrrRj772c9yFIRmhIvQadpcQXODhStbbLXEVloZHbHvh5rIglU+X71hU80+IZWbF5Yr7Xm+TMlWWlqH7breV0bWZkbGVllaL4rFYsmbb76Zf1 -tERAtUTBBPKVzaYcummNaOlNLtVrZXk1LOV6blHV+gXrFPSEVilol2Ti2Idk6LQp1nJ5bs95Y3HD42YCnV7ThONpVKMS0vzRhHQGjaclrW5Sz5taSl45hi1GNiRCNiv -mz558uPFQWjpBR0lKZdLRACIKsc3bH/WM/upsLqobjzS9+298Xj8RGEmWCIiGgBuRObJK7QXm/JFWlbNSqlxUxqz3TF/6VqynHE9Kzq9nABjHpUiytLWo96zR2HRlcf -zCae9i1rj+u6QwjTsBNNG3tpafoXjchFOVs3i57IhV496hE1ojHeY1TeJ2S8t0iFx8sV9YIrDyX1gZOsOzS61Pb8JVrrVtd1Y29/+9uZZY6IaIExMFYArM/barGlLRu -6qg2rbNvGR/pLoyFVox7jP1M58rEARj2qCQQZYzmNR4Ku+PDYcqVUh+M4GV4tNFMcAaHpV84CbaQUfNQsOI8Y6Tjez4yPgizse3UFiBiTFkiziDRqrWNKqSNz0ziCc2 -6JiCbdIM/eHX0Q1rPKiBZTOVo/qT2TSd9HzgCQhT3qUVPGIlDGxEXQKCLNSqkMgJd49REDEDotPOD+Q4HZYQGrXaW0VC+yU7UpCidl+ihPuRINUcepkCelETQTh05Yg -ZspnszUPF/t95Fv4jivZFDwPeyzCmMHGtIHi47lhfEI99ghIlqIxhD4vuhHDwfmxzGDloSomKpcs1GdRKUmm9XktlBO1MkmM2nnototqWrbZqedA4Bi4OMQxry9efvQ -SCI2gsn5togYgNDcO+qb7+wu+B88ptVVykI3bCQDBw6U0ZBAIOXc56W8gOP5zlWpm0qNr/UQU6p8zVTV4HhVbI5XMctxq9Xj/bxE/VP1s1JZ20t1ADIY80efbbZ2v5y -PP+Urec4Ewd4gCMZ8nxkKiYgWmrfi0eBus/HZ3QXvI2NaHbUtrIKFbODAhQUNMQIEEu7p4ZXbM5nYu0og5fWMIqU2ZIp2TgCYU2vnpnpMdDtnKtu0k2rnAGDY9r1f5b -Hv2abEz8cc6xljgpeDIBji1UIMQOi0+WUwPNjsJT73gqN+NNxot3t1iUY/6eaNpWMQUaWOF5HoinOiJpTx+jAiCKitTktHJKoiluNUw1I5OF/5vkQm79k0RQU+6ZwYq -ayqAxgcte3CobrU4LBjH/SD4CXP854fGxsb/qd/+qc5mSol7HUiIppTV/uPFP4FG761X7znjuV0V7Ex1uSl4/XGthJQojF5Z6njt3PlW/op2rrjtnMGER1k02jnItu6 -6EBl0vuveIyBwYjW/sFM/OhQOn4oMMEe3/d/WSgUDvFKIQYgdNq8yzxh4OHom7df+5iTzz7pxuIJbemkKGWLhN1Ak+pZkUkVZ2nAY3INaCojkeMLf9RIZTeNVOwqK1M -/TmCmeA0RlM+Vnr/mtj/syJLx36n8M4ExMEEQBL5X8H1/aGxsbGh4eLjAK4WIaOF6s//Dsbdsv+LnuqH+l24yEbdsO6mUckVEl2MCU9EGVd/0I6o5MtNv58pPJBW7nx -9vQpc5bjs30dZG/VzpNUTKmx2aiZ8NjDHGBEXf84cLhcLRkZGRUV4lNFPsSaVTcv3110ssFhPXdZXWWsqVlzEGxpiwUhaprNgmKtEKQVC7b59SKqLuNuWvci2JcH8oN -f5a5eevrFzLj4t6vimef9L7P95reJ4Hz/MQlAwPDwe33347F4oTEb1K3HLLLcp1XWXb9qR2rvrm/XjtXFQ7VPmY6nYpCAKpbA+r26HKnxURBEFw3HZORMb/b4wZ//nK -di3qNQBUtnMmCAJ/bGws+MQnPsF2joiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIi -IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI -iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIvqGMJ -AAAIABJREFUiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiICDDGbDSzq6Xiub9d -cXzHKb7Pf6t4rl385E663B6vKLfl0z1PRHSm6+SztR5ju8e/I6IyxSIgIqJp3GCcb4z5MN87P3MiopmyXoW/01EA3zrO+RSADRXfPwggOM7PF3iZEBFvQk0OwD8CeAO -A7/C98zMnImIAUiIiTwHYdZwKdQDAjyoOXSIio2fgfV7Ey4+IFpDFpRvRV+V7Z5089585y/jVj58xnSxOwSIiIiIiIgYgRERERETEAISIiIiIiIgByJlijLnWGPMFY8 -xuY8yYMeZFY8zXjDFvMsbo4zzupFLVGWNeY4z5pDHmF8aYYWPMSMVrvNsY0zjD9/0fFa9/9XF+7k0VP3fYGKOO87PfqvjZ7il+5jxjzAeMMY8aY/YYYwrGmCPGmOeMM -Z83xrzVGOPM4edVTqU8WvreNsb8rjHmB8aYQ8aYUWPM08aYjxhjVk/jeU/p95rD99VkjPm/jTHfMca8Unpfe4wx3zDGvMMYkzzB4x8pva8XS9+/1hjz/dK1+JIx5h5j -zAUzKP/9pe+1MebNxph/L72/4dK1/lFjzIqqxy4zxvyDMebnpb+DQ8aYB40xv3686/JUPyNjzEUV1/U+Y4x1Eq/1RxWP+Yt5co32GWPeb4x52BhzsPT7v1wq+z8sLTi -OetxvGWMMgIcrDm+v+P2+PUfXXrkcDpe+F2PMDcaYL/8f9s47zK6q6sPvnkloAYHQIl2KIoLSRJQeLDSRJl2kCggWFBQhE4ZMwicI+uFnAZQSBAQE6TU0KYKAdFDASq -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 -ieJWgnmOPkWSBJinVcC7eVcX7fshw74jiGYKK/r/22v8AxEeMoUqzlRjEB42Ffqd7ARrNJg8b33aGGxpMdIwdlCT/vMMk/cF5pI6qiOPa+oi9bAydhTPHp7GN/Tru5F -n7nEnyb9xiqkE5cM3cRz8TDC37d+QSzyF+NsaVrIVFGOnuPYF6Lx3zYgLGGf7an5um8BiqKq1rmxR9gWIsIFYyKIiIiEmYJmohnJsW71LVkHowXxZWISIZ+ARERERER -ETVAREREREREDRARERERERE1QERERERERA0QERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE -RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER -ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE -RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER -ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE -RERERERERERERERERERERERERERERERERERERERERERERkQZmZteZ2fVFljnNonvJrzPOv18mwjHc4pddQTHSEGmiU3yY2X3+fa9c73+kYdTPh8EtSsc1D+urfLgWK6 -f28cu9bmaLKORKCuOGy9NdlcdC165+XRFuuc7RzIaa2aDuUhZ287ywUFh397LdzEaZ2VNmFlNpV10K0PIT5e7AQGB4kUXfAx7J+pvjv3sy6/PnFLIiUkUnADOBA8zsV -3nKsl7AWYABRzjnvlewyQ/kOj0IuBDoo9BQWJfpDGA14GjFsjRCRutpZh+Y2fllrv9SoV84SvkFRBo+regXkIXD5Ad1168bhPcwH94vm1k8x/cJ//1FCq3K8/iPOY91 -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 -re7WZ57Du/zRf96IH3+vXfz5rDIjvcIpfxBcIj+xw7hlR/28ySZta/yPqR03Qp6azEeCrlWpf3+lrgHMu5DkQpB3OGdY44qcl1qIR8V1b5aWZ/0zwg0giNkJfN7I1Gb -IB03Ckxs8m+C8McP1zeMP8zeJTji7S+rzyc7ycVmusLqFFZ478XLCD9cHpn+4vT976QvirX8JNRl61iA8SFCsqfZn3eMcHRZrVogJQYDyua2YU+Hr73w5r+O9ds7FGX -rWIYruiHrp7qL7ZPmNm2fj6VWypNx377V/qLzlzfPWGUmfX220x3zHMROqdNfTx869e7IMccB5GXLTFtlnK8dW+AlJGeBpjZ4z58pvu4XsavG3Um9CjlSDnxXEnjuZT -9LZSna5F3u1Eem+kn3rsvlC4uy1HG5Aq3yNeDKA0Q/9k5/pjMzAZG2EakNF1qOosaTyWWJyU3QMq8DkQtBxcK6zxxUpPrUMT0XG4D5G4/bHgvRLqwAXJwtcfnF5Ga59 -txhRr+5S4rIvJjLzN/4OGwgv9lrE2ponr0DEh5rgE+Ag5XUIiIiIj8YB1MMJDAOQoKNUC6lB8J6e/AQVH6pYqIiIhI92JmfXx9r9U/RC9qgHR5I+QW4DbgFIWGiIiIy -A/O/yMYMvwMBYWIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI -iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiEg -B/x9OcNpH2VDkjgAAAABJRU5ErkJggg== - `); - document.body.append(outer); - }, - - Show: function(callback) { - // prepare it - var self = WarnHelper; - if(!self.injected) { - self.injected = true; - self.injectCSS(); - self.injectHTML(); - } - // show it - $("#triggerwarn").addClass("show"); - // wait some time - setTimeout(() => { - // hide it & wait again - $("#triggerwarn").removeClass("show"); - setTimeout(() => { - // call next - callback(); - }, this.animationSeconds * 1000); - }, this.waitSeconds * 1000); - } -}; \ No newline at end of file diff --git a/src/Smallog.ts b/src/Smallog.ts new file mode 100644 index 0000000..4b7a23d --- /dev/null +++ b/src/Smallog.ts @@ -0,0 +1,42 @@ +/** + * @author D.Thiele @https://hexx.one + */ + +export enum LogLevel { + Error = 0, + Info = 1, + Debug = 2 +} + +export module Smallog { + + var logLevel: LogLevel = LogLevel.Info; + var preFix: string = " AudiOrbits: "; + var printTime: boolean = true; + + export function GetLevel() { + return logLevel; + } + + export function SetLevel(level: LogLevel) { + logLevel = level; + } + + export function Error(msg: string) { + Log(console.error, msg); + } + + export function Info(msg: string) { + if (logLevel >= 1) Log(console.info, msg); + } + + export function Debug(msg: string) { + if (logLevel >= 2) Log(console.debug, msg); + } + + function Log(call: any, msg: string) { + var m = preFix+ msg; + if (printTime) m = ("[" + new Date().toLocaleString() + "]") + m; + call(m); + } +} \ No newline at end of file diff --git a/src/WEAS.ts b/src/WEAS.ts index b3beb19..7ad365d 100644 --- a/src/WEAS.ts +++ b/src/WEAS.ts @@ -23,6 +23,8 @@ * */ +import { Smallog } from "./Smallog"; + export class WEAS { // has currently valid audio data stored? @@ -64,7 +66,7 @@ export class WEAS { constructor() { // if wallpaper engine context given, listen if (!window['wallpaperRegisterAudioListener']) { - console.log("'window.wallpaperRegisterAudioListener' not given!"); + Smallog.Info("'window.wallpaperRegisterAudioListener' not given!"); return; } @@ -79,7 +81,7 @@ export class WEAS { // worker Error this.weasWorker.addEventListener("error", (e) => { - console.log("weas error: [" + e.filename + ", Line: " + e.lineno + "] " + e.message); + Smallog.Error("weas error: [" + e.filename + ", Line: " + e.lineno + "] " + e.message); }, true); // intialize wallpaper engine audio listener when laoded @@ -87,7 +89,7 @@ export class WEAS { // check proof if (!audioArray) return; if (audioArray.length != 128) { - console.log("audioListener: received invalid audio data array. Length: " + audioArray.length); + Smallog.Error("audioListener: received invalid audio data array. Length: " + audioArray.length); return; } let audBuff = new Float64Array(audioArray); diff --git a/src/WEICUE.ts b/src/WEICUE.ts index bc3f7b8..dcf0c96 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -18,6 +18,7 @@ * Lighting effects for Corsair ICUE devices. */ +import { Smallog } from "./Smallog"; import { WEAS } from "./WEAS"; export class WEICUE { @@ -55,7 +56,7 @@ export class WEICUE { // Plugin handler window['wallpaperPluginListener'] = { onPluginLoaded: function (name, version) { - console.log("Plugin: " + name + ", Version: " + version); + Smallog.Info("Plugin: " + name + ", Version: " + version + " : registered."); if (name === 'cue') this.isAvailable = true; if (name === 'led') this.isAvailable = true; } @@ -281,6 +282,7 @@ AAAASUVORK5CYII= // show a message by icue icueMessage(msg) { + Smallog.Debug("WEICUE MSG: " + msg); $("#icueholder").css('opacity', 1); $("#icuetext").html(msg); $("#icueholder").fadeIn({ queue: false, duration: "slow" }); @@ -374,7 +376,7 @@ AAAASUVORK5CYII= this.initCUE(0); - console.log("weiCUE: init..."); + Smallog.Debug("weiCUE: init..."); // recreate if reinit if (this.icueInterval) clearInterval(this.icueInterval); diff --git a/weas.js b/weas.js deleted file mode 100644 index fad11e0..0000000 --- a/weas.js +++ /dev/null @@ -1,97 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * WEWWA - * Wallpaper Engine Audio Supplier - * - * DEPENDS ON: - * - "./worker/weasWorker.js" - * - jQuery (window loaded event) - * - Wallpaper Engine Web Wallpaper environment - * - audio-processing supported wallpaper... - * - * This is an aditional JS file to be included in any Wallpaper Engine - * Web-Wallpaper project to make working with audio easier. - * It will automatically start to receive and process the audio data - * which can then be accessed on the global object. - * -*/ - -var weas = { - // has currently valid audio data stored? - hasAudio: function () { - // return false if there is no data or its invalid due to time (> 3s old) - return weas.lastAudio && !weas.lastAudio.silent && - (performance.now() / 1000 - weas.lastAudio.time < 3); - }, - // audio processing worker - weasWorker: null, - // last processed audio object - lastAudio: null, - // settings object - settings: { - audioprocessing: true, - // do pink-noise processing? - equalize: true, - // convert to mono? - mono_audio: true, - // invert low & high freqs? - audio_direction: 0, - // peak filtering - peak_filter: 1, - // neighbour-smoothing value - value_smoothing: 2, - // time-value smoothing ratio - audio_increase: 75, - audio_decrease: 35, - // multipliers - treble_multiplier: 0.5, - mids_multiplier: 0.75, - bass_multiplier: 1.8, - // ignore value leveling for "silent" data - minimum_volume: 0.005, - }, - // function will get called with the audio data as array, containing L & R channels - audioListener: function (audioArray) { - // check proof - if (!audioArray) return; - if (audioArray.length != 128) { - print("audioListener: received invalid audio data array. Length: " + audioArray.length); - return; - } - let audBuff = new Float64Array(audioArray); - // post web worker task - weas.weasWorker.postMessage({ - settings: weas.settings, - last: weas.lastAudio, - audio: audBuff.buffer - }, [audBuff.buffer]); - }, - // task completed - processed: function (e) { - e.data.data = new Float64Array(e.data.data); - weas.lastAudio = e.data; - }, - // task error - errror: function (e) { - console.log("weas error: [" + e.filename + ", Line: " + e.lineno + "] " + e.message); - } -}; - -$(() => { - // if wallpaper engine context given, listen - if (window.wallpaperRegisterAudioListener) { - // initialize web worker - weas.weasWorker = new Worker('./we_utils/worker/weasWorker.js'); - weas.weasWorker.addEventListener("message", weas.processed, true); - weas.weasWorker.addEventListener("error", weas.error, true); - // intialize wallpaper engine audio listener - window.wallpaperRegisterAudioListener(weas.audioListener); - } -}); \ No newline at end of file diff --git a/weicue.js b/weicue.js deleted file mode 100644 index 8833254..0000000 --- a/weicue.js +++ /dev/null @@ -1,495 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @see - * REQUIRES: - * - jQuery - * - * @description - * WEICUE - * Wallpaper Engine iCUE effects for web wallpapers - * - * Uses several different methods to create - * Lighting effects for Corsair ICUE devices. - */ - -var weicue = { - // runtime values - injected: false, - PAUSED: false, - isAvailable: false, - canvasX: 23, - canvasY: 7, - icueDevices: [], - preview: null, - icueInterval: null, - helperCanvas: null, - helperContext: null, - // settings - settings: { - icue_mode: 1, - icue_area_xoff: 50, - icue_area_yoff: 90, - icue_area_width: 75, - icue_area_height: 30, - icue_area_blur: 5, - icue_area_decay: 15, - icue_area_preview: false, - icue_main_color: "0 0.8 0", - // AudiOrbits bg Color, used as "decay"-color - main_color: "0 0 0", - }, - - injectCSS: function () { - var st = document.createElement("style"); - st.innerHTML = ` - #icueholder { - position: absolute; - top: -120px; - left: 0; - width: auto; - height: auto; - margin: 10px; - display: none; - } - #icuelogo { - float: left; - height: 80px; - width: 80px; - } - #icuetext { - float: left; - margin: 25px 5px; - font-size: 175%; - } - #icueholder { - text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5); - } - .cuePreview { - position: absolute; - background: rgba(255, 0, 0, .3); - } - `; - document.head.append(st); - }, - - injectHTML: function () { - var outer = document.createElement("div"); - outer.id = "icueholder"; - var imgg = document.createElement("img"); - imgg.id = "icuelogo"; - imgg.setAttribute("src", ` -data:image/png;base64,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 -AAAASUVORK5CYII= - `); - // make text holder - var txtt = document.createElement("div"); - txtt.id = "icuetext"; - // append image and text - outer.append(imgg, txtt); - document.body.append(outer); - }, - - // show a message by icue - icueMessage: function (msg) { - $("#icueholder").css('opacity', 1); - $("#icuetext").html(msg); - $("#icueholder").fadeIn({ queue: false, duration: "slow" }); - $("#icueholder").animate({ top: "0px" }, "slow"); - setTimeout(() => { - $("#icueholder").fadeOut({ queue: false, duration: "slow" }); - $("#icueholder").animate({ top: "-120px" }, "slow"); - }, 10000); - }, - - // helper - getArea: function (inPx) { - var sett = weicue.settings; - var wwid = window.innerWidth; - var whei = window.innerHeight; - var w = wwid * sett.icue_area_width / 100; - var h = whei * sett.icue_area_height / 100; - var l = ((wwid - w) * sett.icue_area_xoff / 100); - var t = ((whei - h) * sett.icue_area_yoff / 100); - return { - width: w + (inPx ? "px" : ""), - height: h + (inPx ? "px" : ""), - left: l + (inPx ? "px" : ""), - top: t + (inPx ? "px" : ""), - }; - }, - - // get data for icue - getEncodedCanvasImageData: function (imageData) { - var colorArray = []; - for (var d = 0; d < imageData.data.length; d += 4) { - var write = d / 4 * 3; - colorArray[write] = imageData.data[d]; - colorArray[write + 1] = imageData.data[d + 1]; - colorArray[write + 2] = imageData.data[d + 2]; - } - return String.fromCharCode.apply(null, colorArray); - }, - - // canvas blur helper function - gBlurCanvas: function (canvas, ctx, blur) { - var sum = 0; - var delta = 5; - var alpha_left = 1 / (2 * Math.PI * delta * delta); - var step = blur < 3 ? 1 : 2; - - var x, weight; - for (var y = -blur; y <= blur; y += step) { - for (x = -blur; x <= blur; x += step) { - weight = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta)); - sum += weight; - } - } - for (var y = -blur; y <= blur; y += step) { - for (x = -blur; x <= blur; x += step) { - ctx.globalAlpha = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta)) / sum * blur * blur; - ctx.drawImage(canvas, x, y); - } - } - ctx.globalAlpha = 1; - }, - - // show or hide preview - updatePreview: function () { - var self = weicue; - var sett = self.settings; - // create preview? - if (!self.preview && sett.icue_area_preview) { - self.preview = document.createElement("div"); - self.preview.classList.add("cuePreview"); - document.body.appendChild(self.preview); - } - // update settings or destroy - if (self.preview) { - if (!sett.icue_area_preview) { - document.body.removeChild(self.preview); - self.preview = null; - } - else Object.assign(self.preview.style, self.getArea(true)); - } - }, - - init: function () { - var self = weicue; - var sett = self.settings; - - // dont initialize if disabled - if (sett.icue_mode == 0) return; - - // inject necessary HTML & CSS - if (!self.injected) { - self.injected = true; - self.injectCSS(); - self.injectHTML(); - } - - self.showWaiting(); - setTimeout(() => { - self.hideWaiting(); - }, 30000); - - this.initCUE(0); - - print("weiCUE: init..."); - - // recreate if reinit - if (self.icueInterval) clearInterval(self.icueInterval); - if (self.helperCanvas) document.body.removeChild(self.helperCanvas); - // setup canvas - self.helperCanvas = document.createElement("canvas"); - self.helperCanvas.id = "helpCvs"; - self.helperCanvas.width = self.canvasX; - self.helperCanvas.height = self.canvasY; - self.helperCanvas.style.display = "none"; - self.helperContext = self.helperCanvas.getContext("2d"); - document.body.appendChild(self.helperCanvas); - - // update devices about every 33ms/30fps. iCue doesnt really support higher values - self.icueInterval = setInterval(self.updateFrame, 1000 / 30); - }, - - // will initialize ICUE api & usage - initCUE: function (count) { - var self = weicue; - // wait for plugins - if (!self.isAvailable) { - if (count < 100) { - $("#icueholder").animate({ 'opacity': 0.1 + (100 - count) / 115 }, 250); - setTimeout(() => this.initCUE(count + 1), 300); - } - else self.icueMessage("LED: Plugin not found!"); - return; - } - // setup devices - self.icueDevices = []; - window.cue.getDeviceCount((deviceCount) => { - self.icueMessage("LED: Found " + deviceCount + " devices."); - for (var xi = 0; xi < deviceCount; xi++) { - var xl = xi; - window.cue.getDeviceInfo(xl, (info) => { - info.id = xl; - window.cue.getLedPositionsByDeviceIndex(xl, function (leds) { - info.leds = leds; - self.icueDevices[xl] = info; - }); - }); - } - }); - }, - - // do the thing... - updateFrame: function () { - var self = weicue; - var sett = self.settings; - if (self.Paused || !self.isAvailable || sett.icue_mode == 0 || self.icueDevices.length < 1) return; - // projection mode - if (sett.icue_mode == 1) { - // get scaled down image data and encode it for icue - var encDat = self.getEncodedCanvasImageData(self.helperContext.getImageData(0, 0, self.canvasX, self.canvasY)); - // update all icueDevices with data - for (var xi = 0; xi < self.icueDevices.length; xi++) { - window.cue.setLedColorsByImageData(xi, encDat, self.canvasX, self.canvasY); - } - } - // color mode - if (sett.icue_mode == 2) { - // get lol objects - var col = sett.icue_main_color.split(" "); - var ledColor = { - r: col[0] * 255, - g: col[1] * 255, - b: col[2] * 255 - };; - // try audio multiplier processing - if (weas.hasAudio()) { - var aud = weas.lastAudio; - var mlt = 255 * aud.average / aud.range / aud.intensity * 10; - ledColor = { - r: Math.min(255, Math.max(0, col[0] * mlt)), - g: Math.min(255, Math.max(0, col[1] * mlt)), - b: Math.min(255, Math.max(0, col[2] * mlt)) - }; - } - // update all icueDevices with data - for (var xi = 0; xi < self.icueDevices.length; xi++) { - window.cue.setAllLedsColorsAsync(xi, ledColor); - } - } - }, - - // prepare canvas - updateCanvas: function (mainCanvas) { - var self = weicue; - var sett = self.settings; - if (!self.isAvailable || !mainCanvas || sett.icue_mode == 0 || self.icueDevices.length < 1) return; - - if (sett.icue_mode == 1) { - // get helper vars - var cueWid = self.canvasX; - var cueHei = self.canvasY; - var area = self.getArea(); - var hctx = self.helperContext; - // get real rgb values - var spl = sett.main_color.split(' '); - for (var i = 0; i < spl.length; i++) spl[i] *= 255; - // overlay "decay" style - hctx.fillStyle = "rgba(" + spl.join(", ") + ", " + sett.icue_area_decay / 100 + ")"; - hctx.fillRect(0, 0, cueWid, cueHei); - // scale down and copy the image to the helper canvas - hctx.drawImage(mainCanvas, area.left, area.top, area.width, area.height, 0, 0, cueWid, cueHei); - // blur the helper projection canvas - if (sett.icue_area_blur > 0) self.gBlurCanvas(self.helperCanvas, hctx, sett.icue_area_blur); - } - }, - - showWaiting: function () { - $("#icuetext").html("LED: waiting for plugin."); - $("#icueholder").fadeIn({ queue: false, duration: "fast" }); - $("#icueholder").animate({ top: "0px" }, "fast"); - }, - - hideWaiting: function () { - $("#icueholder").fadeOut({ queue: false, duration: "fast" }); - $("#icueholder").animate({ top: "-120px" }, "fast"); - }, -}; - -// Plugin handler -window.wallpaperPluginListener = { - onPluginLoaded: function (name, version) { - print("Plugin: " + name + ", Version: " + version); - if (name === 'cue') weicue.isAvailable = true; - if (name === 'led') weicue.isAvailable = true; - } -}; \ No newline at end of file From 238d64020a6d067a94d1ef1469cf8ee496e3c88b Mon Sep 17 00:00:00 2001 From: hexxone Date: Thu, 19 Nov 2020 17:26:36 +0100 Subject: [PATCH 03/76] improve logging --- wewwa.js | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/wewwa.js b/wewwa.js index 66502a9..017ae58 100644 --- a/wewwa.js +++ b/wewwa.js @@ -42,7 +42,7 @@ var wewwApp = wewwApp || {}; wewwApp.Init = () => { wewwApp.LoadProjectJSON((proj) => { if (proj.type != "web") { - console.log("Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting..."); + console.error("Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting..."); return; } wewwApp.project = proj; @@ -61,7 +61,7 @@ wewwApp.LoadProjectJSON = (complete) => { url: "project.json", beforeSend: (xhr) => xhr.overrideMimeType("text/plain;"), success: (result) => complete(JSON.parse(result)), - error: (xhr, status, error) => console.log(status + ": ajax error!\r\n" + error) + error: (xhr, status, error) => console.error(status + ": ajax error!\r\n" + error) }); } @@ -71,7 +71,7 @@ wewwApp.LoadStorage = () => { if (last != null) { var merged = Object.assign(props, JSON.parse(last)); wewwApp.project.general.properties = merged; - console.log("Loaded & merged settings.") + console.debug("Loaded & merged settings.") } } @@ -432,7 +432,7 @@ wewwApp.CreateItem = (prop, itm) => { sliderVal.setAttribute("id", sliderVal.name); sliderVal.setAttribute("type", "number"); sliderVal.style.width = "75%"; - if(canEdit) { + if (canEdit) { sliderVal.setAttribute("value", itm.value); sliderVal.addEventListener("change", function (e) { wewwApp.SetProperty(prop, this); }); } @@ -466,7 +466,7 @@ wewwApp.CreateItem = (prop, itm) => { td2.prepend(inp); } // append td3 or stretch td2? - if(td3) { + if (td3) { row.append(td1, td2, td3) } else { @@ -489,7 +489,9 @@ wewwApp.SetProperty = (prop, elm) => { var applyCall = (val) => { // save the updated value to storage props[prop].value = val; - console.log("Property set: " + prop + " v: " + val); + + //console.debug("Property set: " + prop + " v: " + val); + // update wewwApp.UpdateSettings(); var obj = {}; @@ -510,7 +512,7 @@ wewwApp.SetProperty = (prop, elm) => { case "slider": if (elm.name.includes("_out_")) { var inpt = document.querySelector("#wewwa_" + prop); - if(inpt) inpt.value = elm.value; + if (inpt) inpt.value = elm.value; else console.error("Slider not found: " + prop); } else { @@ -565,21 +567,26 @@ wewwApp.UpdateSettings = () => { var partials = cprop.split(/&&|\|\|/); // loop all partial values of the check for (var part of partials) { - var prefix ="wewwaProps."; - var onlyVal = part.match(/[a-zA-Z0-9_\.]*/)[0]; - if (!onlyVal.startsWith(prefix) && !onlyVal.startsWith("!" + prefix)){ + var prefix = "wewwaProps."; + var onlyVal = part.match(/[!a-zA-Z0-9_\.]*/)[0]; + if (!onlyVal.startsWith(prefix) && !onlyVal.startsWith("!" + prefix)) { // fix for inverted values var replW = onlyVal; - if(replW.startsWith("!")) { + if (replW.startsWith("!")) { replW = replW.substr(1); prefix = "!" + prefix; } + //console.debug("replace: " + onlyVal + " >> " + prefix + replW); cprop = cprop.replace(onlyVal, prefix + replW); } - + + } + try { + visible = eval(cprop) == true; + } + catch (e) { + console.error("Error: (" + cprop + ") for: " + p + " => " + e); } - visible = eval(cprop) == true; - //console.log("eval: (" + cprop + ")=" + visible + " for: " + p); } @@ -646,7 +653,7 @@ wewwApp.requestMicrophone = () => { wewwApp.source.connect(wewwApp.analyser); wewwApp.startAudioInterval(); }).catch(function (err) { - console.log(err); + console.error(err); if (location.protocol != "https:") { var r = confirm("Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS for this feature to work! Press 'ok'/'yes' to get redirected to HTTPS and try again."); if (r) window.location.href = window.location.href.replace("http", "https"); @@ -695,6 +702,7 @@ wewwApp.initiateAudio = (data) => { // starts audio analyser interval wewwApp.startAudioInterval = () => { var data = new Uint8Array(128); + // 33ms ~~ 30fps wewwApp.audioInterval = setInterval(() => { if (!wewwApp.audioCallback) { wewwApp.stopAudioInterval(); @@ -704,7 +712,9 @@ wewwApp.startAudioInterval = () => { wewwApp.analyser.getByteFrequencyData(data); var stereo = wewwApp.convertAudio(data); wewwApp.audioCallback(stereo); - }, 33); // 33ms ~~ 30fps + }, 33); + // tell Wallpaper we are sending audio + wewwApp.ApplyProp({ audioprocessing: { value: true } }) } // stops audio analyser interval and playing audio @@ -719,9 +729,9 @@ wewwApp.stopAudioInterval = () => { } } -if (window.wallpaperRegisterAudioListener) console.log("WEWWA: detected wallpaper engine - Standby."); +if (window.wallpaperRegisterAudioListener) console.info("[WEWWA] detected wallpaper engine => Standby."); else { - console.log("WEWWA: wallpaper engine not detected - Init!"); + console.info("[WEWWA] wallpaper engine not detected => Init!"); // define audio listener first, so we dont miss when it gets registered. window.wallpaperRegisterAudioListener = function (callback) { // set callback to be called later with analysed audio data From 2c699e8fef054270eb1496241cf9ea945e3d7d9d Mon Sep 17 00:00:00 2001 From: hexxone Date: Fri, 20 Nov 2020 11:43:03 +0100 Subject: [PATCH 04/76] Logger change --- src/Smallog.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Smallog.ts b/src/Smallog.ts index 4b7a23d..d5c5975 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -11,8 +11,8 @@ export enum LogLevel { export module Smallog { var logLevel: LogLevel = LogLevel.Info; - var preFix: string = " AudiOrbits: "; - var printTime: boolean = true; + var preFix: string = "[Smallog] "; + var printTime: boolean = false; export function GetLevel() { return logLevel; @@ -22,6 +22,10 @@ export module Smallog { logLevel = level; } + export function SetPrefix(pre: string) { + preFix = pre; + } + export function Error(msg: string) { Log(console.error, msg); } @@ -35,8 +39,8 @@ export module Smallog { } function Log(call: any, msg: string) { - var m = preFix+ msg; - if (printTime) m = ("[" + new Date().toLocaleString() + "]") + m; - call(m); + var m = msg; + if (printTime) m = ("[" + new Date().toLocaleString() + "] ") + m; + call(preFix + m); } } \ No newline at end of file From 30baaf105517d3f9ab5f9f726b1051c4c0d00855 Mon Sep 17 00:00:00 2001 From: hexxone Date: Sun, 22 Nov 2020 23:08:32 +0100 Subject: [PATCH 05/76] Add and use Base Component and Settings class --- ShaderQuality.js | 71 --------------------------------------------- src/CComponent.ts | 29 +++++++++++++++++++ src/CSettings.ts | 30 +++++++++++++++++++ src/Stats.ts | 7 +++++ src/WEAS.ts | 73 ++++++++++++++++++++++++++--------------------- src/WEICUE.ts | 41 +++++++++++++++----------- src/WarnHelper.ts | 8 ++++++ wewwa.js | 2 -- 8 files changed, 138 insertions(+), 123 deletions(-) delete mode 100644 ShaderQuality.js create mode 100644 src/CComponent.ts create mode 100644 src/CSettings.ts diff --git a/ShaderQuality.js b/ShaderQuality.js deleted file mode 100644 index 7677ff1..0000000 --- a/ShaderQuality.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * helper for globally injecting "precision" strings into THREE.js shaders. - * - * @todo - * - check if three.js overrides this? - * - add "default" option -> choose highest available -*/ - -ShaderQuality = { - - GetAvailable: function () { - // @TODO - }, - - Inject: function (precision, shaders) { - - for (var shade in shaders) { - - var shader = shaders[shade]; - - console.log("Injecting shader: " + shader.shaderID); - - if (shader.vertexShader) { - shader.vertexShader = this.InjectShader(precision, shader.vertexShader); - } - if(shader.fragmentShader) { - shader.fragmentShader = this.InjectShader(precision, shader.fragmentShader); - } - } - }, - - InjectShader: function (precision, shader) { - // raw string - var str0 = "" - + "precision {bprec}p float;\r\n " - + "precision {bprec}p int;\r\n "; - - // replace precisions according to user setting - switch (precision) { - case "high": - str0 = str0.split("{bprec}").join("high"); - str0 += "precision mediump sampler2D;\r\n " - + "precision mediump samplerCube;\r\n "; - break; - case "low": - str0 = str0.split("{bprec}").join("low"); - break; - default: - str0 = str0.split("{bprec}").join("medium"); - break; - } - //finish new quality code - str0 += "//shaderquality"; - - // remove old quality code - var ito = shader.indexOf("//shaderquality"); - if(ito >= 0) shader = shader.substring(ito); - - // inject new quality code - shader = shader.replace("//shaderquality", str0); - return shader; - } -}; \ No newline at end of file diff --git a/src/CComponent.ts b/src/CComponent.ts new file mode 100644 index 0000000..78cf5d7 --- /dev/null +++ b/src/CComponent.ts @@ -0,0 +1,29 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @description + * represents a modular component for Wallpaper engine + * + */ + +import { CSettings } from "./CSettings"; + + export class CComponent { + + public settings: CSettings = null; + + // Important: Append your child objects, for settings to be applied correctly! + public children: CComponent[] = []; + + public GetComponents() { + var list: CComponent[] = [this]; + this.children.forEach((ch) => list.push(...ch.GetComponents())); + return list; + } + + public GetSettings() { + var list: CSettings[] = []; + this.GetComponents().forEach(co => list.push(co.settings)); + return list; + } + } \ No newline at end of file diff --git a/src/CSettings.ts b/src/CSettings.ts new file mode 100644 index 0000000..1904d36 --- /dev/null +++ b/src/CSettings.ts @@ -0,0 +1,30 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Settings interface, used for type-secure setting applying. + * All Settings-classes should be dereived frrom this one. + */ + +import { Smallog } from "./Smallog"; + +export class CSettings { + + // check if a certain key exists on a (dereived) object and the value type matches + public apply(key: string, castedValue: any) { + if (this[key] !== undefined) { + if (typeof this[key] === typeof castedValue) { + this[key] = castedValue; + return true; + } + else Smallog.Error("ISettings Error: invalid type on: '" + key + + "'. Is: '" + typeof this[key] + "', applied: '" + typeof castedValue + "'"); + } + return false; + } +} diff --git a/src/Stats.ts b/src/Stats.ts index 47b9de3..cf4554d 100644 --- a/src/Stats.ts +++ b/src/Stats.ts @@ -1,3 +1,10 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @description TypeScript Wrapper for mrdoob Stats.js + */ + + declare interface Stats { REVISION: number; dom: HTMLDivElement; diff --git a/src/WEAS.ts b/src/WEAS.ts index 7ad365d..9a15f9d 100644 --- a/src/WEAS.ts +++ b/src/WEAS.ts @@ -23,53 +23,54 @@ * */ +import { CComponent } from "./CComponent"; +import { CSettings } from "./CSettings"; import { Smallog } from "./Smallog"; -export class WEAS { +export class WEASettings extends CSettings { + audioprocessing: boolean = true; + // do pink-noise processing? + equalize: boolean = true; + // convert to mono? + mono_audio: boolean = true; + // invert low & high freqs? + audio_direction: number = 0; + // peak filtering + peak_filter: number = 1; + // neighbour-smoothing value + value_smoothing: number = 2; + // time-value smoothing ratio + audio_increase: number = 75; + audio_decrease: number = 35; + // multipliers + treble_multiplier: number = 0.5; + mids_multiplier: number = 0.75; + bass_multiplier: number = 1.8; + // ignore value leveling for "silent" data + minimum_volume: number = 0.005; +} + +export class WEAS extends CComponent { - // has currently valid audio data stored? - hasAudio() { - // return false if there is no data or its invalid due to time (> 3s old) - return this.lastAudio && !this.lastAudio.silent && - (performance.now() / 1000 - this.lastAudio.time < 3); - } // audio processing worker - weasWorker = null; + private weasWorker = null; // last processed audio object - lastAudio = null; + public lastAudio = null; // settings object - settings = { - audioprocessing: true, - // do pink-noise processing? - equalize: true, - // convert to mono? - mono_audio: true, - // invert low & high freqs? - audio_direction: 0, - // peak filtering - peak_filter: 1, - // neighbour-smoothing value - value_smoothing: 2, - // time-value smoothing ratio - audio_increase: 75, - audio_decrease: 35, - // multipliers - treble_multiplier: 0.5, - mids_multiplier: 0.75, - bass_multiplier: 1.8, - // ignore value leveling for "silent" data - minimum_volume: 0.005, - } + public settings: WEASettings = null; + + constructor(settings: WEASettings = new WEASettings()) { + super(); + this.settings = settings; - constructor() { // if wallpaper engine context given, listen if (!window['wallpaperRegisterAudioListener']) { Smallog.Info("'window.wallpaperRegisterAudioListener' not given!"); return; } - + // initialize web worker this.weasWorker = new Worker('./js/worker/weasWorker.js'); @@ -101,4 +102,10 @@ export class WEAS { }, [audBuff.buffer]); })); } + + hasAudio() { + // return false if there is no data or its invalid due to time (> 3s old) + return this.lastAudio && !this.lastAudio.silent && + (performance.now() / 1000 - this.lastAudio.time < 3); + } } \ No newline at end of file diff --git a/src/WEICUE.ts b/src/WEICUE.ts index dcf0c96..ed5be0e 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -18,12 +18,31 @@ * Lighting effects for Corsair ICUE devices. */ +import { CComponent } from "./CComponent"; +import { CSettings } from "./CSettings"; import { Smallog } from "./Smallog"; import { WEAS } from "./WEAS"; -export class WEICUE { +export class CUESettings extends CSettings { + icue_mode: number = 1; + icue_area_xoff: number = 50; + icue_area_yoff: number = 90; + icue_area_width: number = 75; + icue_area_height: number = 30; + icue_area_blur: number = 5; + icue_area_decay: number = 15; + icue_area_preview: boolean = false; + icue_main_color: string = "0 0.8 0"; + // AudiOrbits bg Color; used as "decay"-color aswell + main_color: string = "0 0 0"; +} + +export class WEICUE extends CComponent { + + private weas: WEAS = null; + + public settings: CUESettings = null; - weas : WEAS = null; // runtime values PAUSED = false; isAvailable = false; @@ -35,23 +54,11 @@ export class WEICUE { helperCanvas = null; helperContext = null; - // settings - settings = { - icue_mode: 1, - icue_area_xoff: 50, - icue_area_yoff: 90, - icue_area_width: 75, - icue_area_height: 30, - icue_area_blur: 5, - icue_area_decay: 15, - icue_area_preview: false, - icue_main_color: "0 0.8 0", - // AudiOrbits bg Color, used as "decay"-color - main_color: "0 0 0", - } - constructor(weas: WEAS) { + constructor(weas: WEAS, settings: CUESettings = new CUESettings()) { + super(); this.weas = weas; + this.settings = settings; // Plugin handler window['wallpaperPluginListener'] = { diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 8173d82..96db191 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -1,5 +1,13 @@ /** * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Displays a seizure warning image centered on html for a given Time. */ export class WarnHelper { diff --git a/wewwa.js b/wewwa.js index 017ae58..6bb7154 100644 --- a/wewwa.js +++ b/wewwa.js @@ -33,8 +33,6 @@ * - react to changes made in the ui and update them in the wallpaper * - save changes made in the ui to localStorage * - * @todo - * - check for correct audio data */ var wewwApp = wewwApp || {}; From 4ec5801fa2f261b244325955f122c6e8e7687319 Mon Sep 17 00:00:00 2001 From: hexxone Date: Tue, 24 Nov 2020 12:13:47 +0100 Subject: [PATCH 06/76] Test worker idk --- src/CComponent.ts | 10 ++ src/Ready.ts | 23 +++++ src/ReloadHelper.ts | 4 +- src/WEAS.ts | 28 ++++-- src/WEAS.worker.ts | 204 +++++++++++++++++++++++++++++++++++++++++ src/WEICUE.ts | 10 +- src/WarnHelper.ts | 4 +- src/worker-loader.d.ts | 10 ++ wewwa.js | 12 ++- 9 files changed, 289 insertions(+), 16 deletions(-) create mode 100644 src/Ready.ts create mode 100644 src/WEAS.worker.ts create mode 100644 src/worker-loader.d.ts diff --git a/src/CComponent.ts b/src/CComponent.ts index 78cf5d7..b488565 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -26,4 +26,14 @@ import { CSettings } from "./CSettings"; this.GetComponents().forEach(co => list.push(co.settings)); return list; } + + // WARNING: this merely returns an object copy!! Has no method members!! + public GetSettingsObj() { + let result = {} + Object.getOwnPropertyNames(this.settings).forEach(p => { + if(typeof this.settings[p] == "function") return; + result[p] = this.settings[p]; + }); + return result; + } } \ No newline at end of file diff --git a/src/Ready.ts b/src/Ready.ts new file mode 100644 index 0000000..4ff1524 --- /dev/null +++ b/src/Ready.ts @@ -0,0 +1,23 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Shorthand Document ready wrapper + */ + +export module Ready { + export function On(fn: any) { + if (typeof fn !== 'function') + return false; + // If document is already loaded, run method + if (document.readyState === 'interactive' || document.readyState === 'complete') + return fn(); + // Otherwise, wait until document is loaded + document.addEventListener('DOMContentLoaded', fn, false); + }; +} diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index 81c3617..102e475 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -2,12 +2,14 @@ * @author D.Thiele @https://hexx.one */ +import { Ready } from "./Ready"; + export class ReloadHelper { waitSeconds = 3; constructor() { - $(() => { + Ready.On(() => { this.injectCSS(); this.injectHTML(); }); diff --git a/src/WEAS.ts b/src/WEAS.ts index 9a15f9d..22fce8b 100644 --- a/src/WEAS.ts +++ b/src/WEAS.ts @@ -25,8 +25,12 @@ import { CComponent } from "./CComponent"; import { CSettings } from "./CSettings"; +import { Ready } from "./Ready"; import { Smallog } from "./Smallog"; +import Worker from 'worker-loader!./WEAS.worker'; + + export class WEASettings extends CSettings { audioprocessing: boolean = true; // do pink-noise processing? @@ -59,12 +63,17 @@ export class WEAS extends CComponent { public lastAudio = null; // settings object - public settings: WEASettings = null; + public settings: WEASettings = new WEASettings(); - constructor(settings: WEASettings = new WEASettings()) { + constructor() { super(); - this.settings = settings; + // delay audio initialization for some time + Ready.On(() => { + setTimeout(() => this.realInit(), 3000); + }); + } + private realInit() { // if wallpaper engine context given, listen if (!window['wallpaperRegisterAudioListener']) { Smallog.Info("'window.wallpaperRegisterAudioListener' not given!"); @@ -72,12 +81,13 @@ export class WEAS extends CComponent { } // initialize web worker - this.weasWorker = new Worker('./js/worker/weasWorker.js'); + this.weasWorker = new Worker(); // worker event data this.weasWorker.addEventListener("message", (e) => { e.data.data = new Float64Array(e.data.data); this.lastAudio = e.data; + Smallog.Debug("Got Data from Worker: " + JSON.stringify(this.lastAudio)); }, true); // worker Error @@ -85,8 +95,7 @@ export class WEAS extends CComponent { Smallog.Error("weas error: [" + e.filename + ", Line: " + e.lineno + "] " + e.message); }, true); - // intialize wallpaper engine audio listener when laoded - $(() => window['wallpaperRegisterAudioListener']((audioArray) => { + window['wallpaperRegisterAudioListener']((audioArray) => { // check proof if (!audioArray) return; if (audioArray.length != 128) { @@ -94,13 +103,16 @@ export class WEAS extends CComponent { return; } let audBuff = new Float64Array(audioArray); + + + Smallog.Debug("Sent Data to Worker: " + JSON.stringify(audioArray)); // post web worker task this.weasWorker.postMessage({ - settings: this.settings, + settings: this.GetSettingsObj(), last: this.lastAudio, audio: audBuff.buffer }, [audBuff.buffer]); - })); + }); } hasAudio() { diff --git a/src/WEAS.worker.ts b/src/WEAS.worker.ts new file mode 100644 index 0000000..c17ef73 --- /dev/null +++ b/src/WEAS.worker.ts @@ -0,0 +1,204 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Wallaper Engine Audio Supplier worker. + */ + +const pinkNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, + 0.5452348949654, 0.50723325864167, 0.4677726234682, 0.44204182748767, 0.41956517802157, + 0.41517375040002, 0.41312118577934, 0.40618363960446, 0.39913707474975, 0.38207008614508, + 0.38329789106488, 0.37472136606245, 0.36586428412968, 0.37603017335105, 0.39762590761573, + 0.39391828858591, 0.37930603769622, 0.39433365764563, 0.38511504613859, 0.39082579241834, + 0.3811852720504, 0.40231453727161, 0.40244151133175, 0.39965366884521, 0.39761103827545, + 0.51136400422212, 0.66151212038954, 0.66312205226679, 0.7416276690995, 0.74614971301133, + 0.84797007577483, 0.8573583910469, 0.96382997811663, 0.99819377577185, 1.0628692615814, + 1.1059083969751, 1.1819808497335, 1.257092297208, 1.3226521464753, 1.3735992532905, + 1.4953223705889, 1.5310064942373, 1.6193923584808, 1.7094805527135, 1.7706604552218, + 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, + 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, + 2.4043566176474, 2.4280476777842, 2.3917477397336, 2.4032522546622, 2.3614180150678]; + +class AudioProcessor { + + public runWithSettings(data, settings, lastData?) { + + // fix pink noise? + if (settings.equalize) this.correctPinkNoise(data); + + // write botch channels to mono + if (settings.mono_audio) this.stereoToMono(data); + + // normal high & low mapping + if (settings.audio_direction == 0) { + // only flip the second half of the data + if (!settings.mono_audio) this.invertSecond(data); + } + // flipped high & low mapping + else { + // flip whole range + if (settings.mono_audio) this.invertAll(data); + // only flip first half of stereo + else this.invertFirst(data); + } + + // process peaks? + if (settings.peak_filter > 0) this.peakFilter(data, settings.peak_filter + 1); + + // smooth data? + if (settings.value_smoothing > 0) this.smoothArray(data, settings.value_smoothing); + + // process with last data? + if (lastData) this.applyValueLeveling(data, lastData.data, settings); + } + + // correct pink noise for first and second half + private correctPinkNoise(data) { + var correct = []; + for (var i = 0; i < 64; i++) { + correct[i] = data[i] / pinkNoise[i]; + correct[64 + i] = data[64 + i] / pinkNoise[i]; + } + return correct; + } + + // merge first and second half into full range + private stereoToMono(data) { + var mono = []; + var mIdx = 0; + for (var i = 0; i < 64; i++) { + mono[mIdx++] = data[i]; + mono[mIdx++] = data[64 + i]; + } + return mono; + } + + // switch front with back in first half + private invertFirst(data) { + for (var i = 0; i < 32; i++) { + var a = data[i]; + data[i] = data[64 - i]; + data[64 - i] = a; + } + } + + // switch front with back in second half + private invertSecond(data) { + for (var i = 0; i < 32; i++) { + var b = data[64 + i]; + data[64 + i] = data[128 - i]; + data[128 - i] = b; + } + } + + // switch front with back in full range + private invertAll(data) { + for (var i = 0; i < 64; i++) { + var a = data[i]; + data[i] = data[128 - i]; + data[128 - i] = a; + } + } + + // filter peaks for full range + private peakFilter(array, amount) { + var oldMax = 0; + var newMax = 0; + var newArray = new Float64Array(array.length); + // pow this shit + for (var i = 0; i < array.length; i++) { + if (array[i] > oldMax) oldMax = array[i]; + newArray[i] = Math.pow(array[i] * amount, amount); + if (newArray[i] > newMax) newMax = newArray[i]; + } + // re-scale & apply + var divide = newMax / oldMax; + for (var i = 0; i < array.length; i++) + array[i] = newArray[i] / divide; + } + + // smooth values for full range + private smoothArray(array, smoothing) { + var newArray = new Float64Array(array.length); + // make smoothed array + for (var i = 0; i < array.length; i++) { + var sum = 0; + for (var index = i - smoothing; index <= i + smoothing; index++) + sum += array[index < 0 ? index + array.length : index % array.length]; + newArray[i] = sum / ((smoothing * 2) + 1); + } + // copy new data to old array + for (var i = 0; i < array.length; i++) + array[i] = newArray[i]; + } + + // function will apply setting-defined data smoothing + private applyValueLeveling(curr, prev, sett) { + for (var i = 0; i < curr.length; i++) { + var diff = curr[i] - prev[i]; + var mlt = 100 - (diff > 0 ? sett.audio_increase : sett.audio_decrease); + curr[i] -= diff * mlt / 100; + } + } +} + +// make audioProcessor +const proc = new AudioProcessor(); + +// get the typed object +const ctx: Worker = self as any; + +console.error("In Worker call!"); + +ctx.addEventListener("message", (e) => { + + console.error("In Worker with data callback!"); + + let eventData = e.data; + // can be null + let data = new Float64Array(eventData.audio); + let lastData = eventData.last; + let settings = eventData.settings; + + proc.runWithSettings(data, settings, lastData); + + // process current frequency data and previous if given + var sum = 0, min = 1, max = 0, bass = 0, mids = 0, peaks = 0; + for (var i = 0; i < data.length; i++) { + // parse current freq value + var idata = data[i]; + // fix null values + if (idata == null || isNaN(idata)) data[i] = idata = 0.0; + // process min max value + if (idata < min) min = idata; + if (idata > max) max = idata; + // process ranges + if (i < 10) bass += idata * settings.bass_multiplier; + else if (i > 70) peaks += idata * settings.treble_multiplier; + else mids += idata * settings.mids_multiplier; + // calc peak average + sum += idata; + } + // calc average with previous entry + var average = sum / data.length; + // done + ctx.postMessage({ + bass: bass, + mids: mids, + peaks: peaks, + sum: sum, + min: min, + max: max, + average: average, + range: max - min, + silent: (max < settings.minimum_volume / 1000), + intensity: (bass * 8 - mids + peaks) / 6 / average, + time: performance.now() / 1000, + data: data.buffer, + }, [data.buffer]); +}); \ No newline at end of file diff --git a/src/WEICUE.ts b/src/WEICUE.ts index ed5be0e..7655859 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -20,6 +20,7 @@ import { CComponent } from "./CComponent"; import { CSettings } from "./CSettings"; +import { Ready } from "./Ready"; import { Smallog } from "./Smallog"; import { WEAS } from "./WEAS"; @@ -41,7 +42,7 @@ export class WEICUE extends CComponent { private weas: WEAS = null; - public settings: CUESettings = null; + public settings: CUESettings = new CUESettings(); // runtime values PAUSED = false; @@ -55,10 +56,9 @@ export class WEICUE extends CComponent { helperContext = null; - constructor(weas: WEAS, settings: CUESettings = new CUESettings()) { + constructor(weas: WEAS) { super(); this.weas = weas; - this.settings = settings; // Plugin handler window['wallpaperPluginListener'] = { @@ -68,7 +68,7 @@ export class WEICUE extends CComponent { if (name === 'led') this.isAvailable = true; } } - $(() => { + Ready.On(() => { // inject helpers this.injectCSS(); this.injectHTML(); @@ -398,7 +398,7 @@ AAAASUVORK5CYII= document.body.appendChild(this.helperCanvas); // update devices about every 33ms/30fps. iCue doesnt really support higher values - this.icueInterval = setInterval(this.updateFrame, 1000 / 30); + this.icueInterval = setInterval(() => this.updateFrame(), 1000 / 30); } // will initialize ICUE api & usage diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 96db191..3c964e0 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -10,13 +10,15 @@ * Displays a seizure warning image centered on html for a given Time. */ +import { Ready } from "./Ready"; + export class WarnHelper { animationSeconds = 1; waitSeconds = 10; constructor() { - $(() => { + Ready.On(() => { this.injectCSS(); this.injectHTML(); }); diff --git a/src/worker-loader.d.ts b/src/worker-loader.d.ts new file mode 100644 index 0000000..2e3c733 --- /dev/null +++ b/src/worker-loader.d.ts @@ -0,0 +1,10 @@ +declare module 'worker-loader!*' { + // You need to change `Worker`, if you specified a different value for the `workerType` option + class WebpackWorker extends Worker { + constructor(); + } + + // Uncomment this if you set the `esModule` option to `false` + //export = WebpackWorker; + export default WebpackWorker; +} \ No newline at end of file diff --git a/wewwa.js b/wewwa.js index 6bb7154..4d4c0c4 100644 --- a/wewwa.js +++ b/wewwa.js @@ -735,6 +735,16 @@ else { // set callback to be called later with analysed audio data wewwApp.audioCallback = callback; } + // ready helper + const ready = function (fn) { + if (typeof fn !== 'function') + return false; + // If document is already loaded, run method + if (document.readyState === 'interactive' || document.readyState === 'complete') + return fn(); + // Otherwise, wait until document is loaded + document.addEventListener('DOMContentLoaded', fn, false); + } // intialize when ready - $(() => wewwApp.Init()); + ready(() => wewwApp.Init()); } \ No newline at end of file From cd304d4b2628c2480912a56c2d7dcccd1d364cd0 Mon Sep 17 00:00:00 2001 From: hexxone Date: Tue, 24 Nov 2020 20:57:08 +0100 Subject: [PATCH 07/76] WebWorker fixxes --- src/WEAS.ts | 27 ++-- src/{WEAS.worker.ts => WEASWorker.ts} | 13 +- worker/weasWorker.js | 185 -------------------------- 3 files changed, 17 insertions(+), 208 deletions(-) rename src/{WEAS.worker.ts => WEASWorker.ts} (96%) delete mode 100644 worker/weasWorker.js diff --git a/src/WEAS.ts b/src/WEAS.ts index 22fce8b..228a3de 100644 --- a/src/WEAS.ts +++ b/src/WEAS.ts @@ -28,8 +28,7 @@ import { CSettings } from "./CSettings"; import { Ready } from "./Ready"; import { Smallog } from "./Smallog"; -import Worker from 'worker-loader!./WEAS.worker'; - +import WEASWorker from 'worker-loader!./WEASWorker'; export class WEASettings extends CSettings { audioprocessing: boolean = true; @@ -57,7 +56,7 @@ export class WEASettings extends CSettings { export class WEAS extends CComponent { // audio processing worker - private weasWorker = null; + private weasWorker: WEASWorker = null; // last processed audio object public lastAudio = null; @@ -75,25 +74,26 @@ export class WEAS extends CComponent { private realInit() { // if wallpaper engine context given, listen - if (!window['wallpaperRegisterAudioListener']) { + if (!['wallpaperRegisterAudioListener']) { Smallog.Info("'window.wallpaperRegisterAudioListener' not given!"); return; } // initialize web worker - this.weasWorker = new Worker(); + this.weasWorker = new WEASWorker(); + var self = this; // worker event data - this.weasWorker.addEventListener("message", (e) => { - e.data.data = new Float64Array(e.data.data); - this.lastAudio = e.data; - Smallog.Debug("Got Data from Worker: " + JSON.stringify(this.lastAudio)); - }, true); + this.weasWorker.onmessage = (e) => { + e.data.data = new Float32Array(e.data.data); + self.lastAudio = e.data; + Smallog.Debug("Got Data from Worker: " + JSON.stringify(e.data)); + }; // worker Error - this.weasWorker.addEventListener("error", (e) => { + this.weasWorker.onerror = (e) => { Smallog.Error("weas error: [" + e.filename + ", Line: " + e.lineno + "] " + e.message); - }, true); + }; window['wallpaperRegisterAudioListener']((audioArray) => { // check proof @@ -102,9 +102,8 @@ export class WEAS extends CComponent { Smallog.Error("audioListener: received invalid audio data array. Length: " + audioArray.length); return; } - let audBuff = new Float64Array(audioArray); + let audBuff = new Float32Array(audioArray); - Smallog.Debug("Sent Data to Worker: " + JSON.stringify(audioArray)); // post web worker task this.weasWorker.postMessage({ diff --git a/src/WEAS.worker.ts b/src/WEASWorker.ts similarity index 96% rename from src/WEAS.worker.ts rename to src/WEASWorker.ts index c17ef73..c598738 100644 --- a/src/WEAS.worker.ts +++ b/src/WEASWorker.ts @@ -109,7 +109,7 @@ class AudioProcessor { private peakFilter(array, amount) { var oldMax = 0; var newMax = 0; - var newArray = new Float64Array(array.length); + var newArray = new Float32Array(array.length); // pow this shit for (var i = 0; i < array.length; i++) { if (array[i] > oldMax) oldMax = array[i]; @@ -124,7 +124,7 @@ class AudioProcessor { // smooth values for full range private smoothArray(array, smoothing) { - var newArray = new Float64Array(array.length); + var newArray = new Float32Array(array.length); // make smoothed array for (var i = 0; i < array.length; i++) { var sum = 0; @@ -153,15 +153,10 @@ const proc = new AudioProcessor(); // get the typed object const ctx: Worker = self as any; -console.error("In Worker call!"); - ctx.addEventListener("message", (e) => { - - console.error("In Worker with data callback!"); - let eventData = e.data; // can be null - let data = new Float64Array(eventData.audio); + let data = new Float32Array(eventData.audio); let lastData = eventData.last; let settings = eventData.settings; @@ -201,4 +196,4 @@ ctx.addEventListener("message", (e) => { time: performance.now() / 1000, data: data.buffer, }, [data.buffer]); -}); \ No newline at end of file +}); diff --git a/worker/weasWorker.js b/worker/weasWorker.js deleted file mode 100644 index 8c7d296..0000000 --- a/worker/weasWorker.js +++ /dev/null @@ -1,185 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * Wallaper Engine Audio Supplier worker. - */ - -var pinkNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, - 0.5452348949654, 0.50723325864167, 0.4677726234682, 0.44204182748767, 0.41956517802157, - 0.41517375040002, 0.41312118577934, 0.40618363960446, 0.39913707474975, 0.38207008614508, - 0.38329789106488, 0.37472136606245, 0.36586428412968, 0.37603017335105, 0.39762590761573, - 0.39391828858591, 0.37930603769622, 0.39433365764563, 0.38511504613859, 0.39082579241834, - 0.3811852720504, 0.40231453727161, 0.40244151133175, 0.39965366884521, 0.39761103827545, - 0.51136400422212, 0.66151212038954, 0.66312205226679, 0.7416276690995, 0.74614971301133, - 0.84797007577483, 0.8573583910469, 0.96382997811663, 0.99819377577185, 1.0628692615814, - 1.1059083969751, 1.1819808497335, 1.257092297208, 1.3226521464753, 1.3735992532905, - 1.4953223705889, 1.5310064942373, 1.6193923584808, 1.7094805527135, 1.7706604552218, - 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, - 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, - 2.4043566176474, 2.4280476777842, 2.3917477397336, 2.4032522546622, 2.3614180150678]; - -// correct pink noise for first and second half -var correctPinkNoise = function (data) { - var correct = []; - for (var i = 0; i < 64; i++) { - correct[i] = data[i] / pinkNoise[i]; - correct[64 + i] = data[64 + i] / pinkNoise[i]; - } - return correct; -}; - -// merge first and second half into full range -var stereoToMono = function (data) { - var mono = []; - var mIdx = 0; - for (var i = 0; i < 64; i++) { - mono[mIdx++] = data[i]; - mono[mIdx++] = data[64 + i]; - } - return mono; -}; - -// switch front with back in first half -var invertFirst = function (data) { - for (var i = 0; i < 32; i++) { - var a = data[i]; - data[i] = data[64 - i]; - data[64 - i] = a; - } -}; - -// switch front with back in second half -var invertSecond = function (data) { - for (var i = 0; i < 32; i++) { - var b = data[64 + i]; - data[64 + i] = data[128 - i]; - data[128 - i] = b; - } -}; - -// switch front with back in full range -var invertAll = function (data) { - for (var i = 0; i < 64; i++) { - var a = data[i]; - data[i] = data[128 - i]; - data[128 - i] = a; - } -}; - -// filter peaks for full range -var peakFilter = function (array, amount) { - var oldMax = 0; - var newMax = 0; - var newArray = new Float64Array(array.length); - // pow this shit - for (var i = 0; i < array.length; i++) { - if (array[i] > oldMax) oldMax = array[i]; - newArray[i] = Math.pow(array[i] * amount, amount); - if (newArray[i] > newMax) newMax = newArray[i]; - } - // re-scale & apply - var divide = newMax / oldMax; - for (var i = 0; i < array.length; i++) - array[i] = newArray[i] / divide; -}; - -// smooth values for full range -var smoothArray = function (array, smoothing) { - var newArray = new Float64Array(array.length); - // make smoothed array - for (var i = 0; i < array.length; i++) { - var sum = 0; - for (var index = i - smoothing; index <= i + smoothing; index++) - sum += array[index < 0 ? index + array.length : index % array.length]; - newArray[i] = sum / ((smoothing * 2) + 1); - } - // copy new data to old array - for (var i = 0; i < array.length; i++) - array[i] = newArray[i]; -}; - -// function will apply setting-defined data smoothing -var applyValueLeveling = function (curr, prev, sett) { - for (var i = 0; i < curr.length; i++) { - var diff = curr[i] - prev[i]; - var mlt = 100 - (diff > 0 ? sett.audio_increase : sett.audio_decrease); - curr[i] -= diff * mlt / 100; - } -}; - -onmessage = function (e) { - let eventData = e.data; - // can be null - let data = new Float64Array(eventData.audio); - let lastData = eventData.last; - let settings = eventData.settings; - - // fix pink noise? - if (settings.equalize) correctPinkNoise(data); - - // write botch channels to mono - if (settings.mono_audio) stereoToMono(data); - - // normal high & low mapping - if (settings.audio_direction == 0) { - // only flip the second half of the data - if (!settings.mono_audio) invertSecond(data); - } - // flipped high & low mapping - else { - // flip whole range - if (settings.mono_audio) invertAll(data); - // only flip first half of stereo - else invertFirst(data); - } - - // process peaks? - if (settings.peak_filter > 0) peakFilter(data, settings.peak_filter + 1); - - // smooth data? - if (settings.value_smoothing > 0) smoothArray(data, settings.value_smoothing); - - // process with last data? - if (lastData) applyValueLeveling(data, lastData.data, settings); - - // process current frequency data and previous if given - var sum = 0, min = 1, max = 0, bass = 0, mids = 0, peaks = 0; - for (var i = 0; i < data.length; i++) { - // parse current freq value - var idata = parseFloat(data[i]); - // fix null values - if (idata == null || isNaN(idata)) data[i] = idata = 0.0; - // process min max value - if (idata < min) min = idata; - if (idata > max) max = idata; - // process ranges - if (i < 10) bass += idata * settings.bass_multiplier; - else if (i > 70) peaks += idata * settings.treble_multiplier; - else mids += idata * settings.mids_multiplier; - // calc peak average - sum += idata; - } - // calc average with previous entry - var average = sum / data.length; - // done - self.postMessage({ - bass: bass, - mids: mids, - peaks: peaks, - sum: sum, - min: min, - max: max, - average: average, - range: max - min, - silent: (max < settings.minimum_volume / 1000), - intensity: (bass * 8 - mids + peaks) / 6 / average, - time: performance.now() / 1000, - data: data.buffer, - }, [data.buffer]); -}; // Strassenbande From 8b9ce8f28cb63ffc0c6e8a8695902ee7b8f74b4f Mon Sep 17 00:00:00 2001 From: hexxone Date: Sat, 28 Nov 2020 14:57:25 +0100 Subject: [PATCH 08/76] Make WEWWA TS, Add Offline Service Worker Plugin --- src/OfflineHelper.ts | 38 +++ src/OfflinePlugin.js | 95 ++++++ src/OfflineWorker.ts | 157 +++++++++ src/WEWWA.ts | 755 +++++++++++++++++++++++++++++++++++++++++++ wewwa.js | 750 ------------------------------------------ 5 files changed, 1045 insertions(+), 750 deletions(-) create mode 100644 src/OfflineHelper.ts create mode 100644 src/OfflinePlugin.js create mode 100644 src/OfflineWorker.ts create mode 100644 src/WEWWA.ts delete mode 100644 wewwa.js diff --git a/src/OfflineHelper.ts b/src/OfflineHelper.ts new file mode 100644 index 0000000..ee2c891 --- /dev/null +++ b/src/OfflineHelper.ts @@ -0,0 +1,38 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Helper class for loading and registering the ServiceWorker + * + * the workerPath is the path to the compiled OfflineWorker js file, relative from the calling html file... + * the "?jsonPath=" argument will give the worker a "public available" json file, consisting of a string array. + * the array will contain all necessary files to run the app offline. + * the ServiceWorker will cache these files and automatically load them if the website is ran offline. + * ServiceWorker is a progressive technology. Some browsers will be unsupported... + */ + +import { Smallog } from "./Smallog"; + +// this should pack the serviceworker like a webwoker. +import OfflineWorker from 'worker-loader!./OfflineWorker'; + +export module OfflineHelper { + // function helper, so OfflineWorker is actually packed + function DontRemove() { return new OfflineWorker() }; + + export async function Register(workerPath: string = "OfflineWorker.worker.js?jsonPath=offlinefiles.json") { + if ('serviceWorker' in navigator) { + await navigator.serviceWorker.register(workerPath).then( + () => Smallog.Info('[OfflineHelper]: service-worker registration complete.'), + () => Smallog.Error('[OfflineHelper]: service-worker registration failure.')); + return true; + } + else Smallog.Info('[OfflineHelper]: service-worker is not supported.'); + return false; + } +} \ No newline at end of file diff --git a/src/OfflinePlugin.js b/src/OfflinePlugin.js new file mode 100644 index 0000000..5cb9419 --- /dev/null +++ b/src/OfflinePlugin.js @@ -0,0 +1,95 @@ + + +const fs = require("fs"); +const validate = require('schema-utils'); + +// eslint-disable-next-line global-require +const { RawSource } = require('webpack-sources'); + +const pluginName = 'OfflinePlugin'; + +// schema for options object +const schema = { + type: 'object', + properties: { + outdir: { + type: 'string' + }, + outfile: { + type: 'string' + }, + extrafiles: { + type: 'array' + } + } +}; + +// list files recursively +function getAllFiles(baseDir, subDir, arrayOfFiles) { + var sub = baseDir + "/" + subDir; + var files = fs.readdirSync(sub); + var arrayOfFiles = arrayOfFiles || []; + files.forEach((file) => { + var fle = sub + "/" + file; + if (fs.statSync(fle).isDirectory()) + arrayOfFiles = getAllFiles(baseDir, subDir + "/" + file, arrayOfFiles); + else + arrayOfFiles.push(subDir + "/" + file); + }); + return arrayOfFiles; +} + +// actual webpack plugin +class OfflinePlugin { + + options = {}; + + constructor(options = {}) { + validate.validate(schema, options); + this.options = options; + } + + // Define `apply` as its prototype method which is supplied with compiler as its argument + apply(compiler) { + var addedOnce = false; + // Specify the event hook to attach to + compiler.hooks.thisCompilation.tap(pluginName, (compilation) => + compilation.hooks.processAssets.tap({ + name: pluginName, + stage: -2000, + }, () => { + if (addedOnce) return; + addedOnce = true; + + console.log('This is an experimental plugin!'); + + // list of all app-contents + var filelist = []; + + // add static files from folder + var sFiles = getAllFiles(this.options.outdir, ""); + for (var staticFile in sFiles) { + filelist.push(sFiles[staticFile]); + } + + // Loop through all compiled assets, + // adding a new line item for each filename. + for (var filename in compilation.assets) { + filelist.push('/' + filename); + } + + // add additional files anyway? + if (this.options.extrafiles) + this.options.extrafiles.map(ef => filelist.push(ef)); + + // create the target file with all app-contents as json list + const jList = JSON.stringify(filelist, null, 1); + compilation.emitAsset(this.options.outfile, new RawSource(jList)); + + console.info("[OfflinePlugin] successfull: " + jList); + } + )); + } +} + +module.exports = OfflinePlugin; \ No newline at end of file diff --git a/src/OfflineWorker.ts b/src/OfflineWorker.ts new file mode 100644 index 0000000..67bcc1a --- /dev/null +++ b/src/OfflineWorker.ts @@ -0,0 +1,157 @@ +/** + * + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Generic Service Worker for Caching an app and making it available offline. + * Needs to be passed the `?jsonPath=` argument with a path to a file json. + * + * @see + * Read more: https://ponyfoo.com/articles/progressive-networking-serviceworker + */ + +"use strict"; + +const wrk: ServiceWorker = self as any; + +console.info('[OfflineWorker] : executing.'); + +// A version number is useful when updating the worker logic, +// allowing you to remove outdated cache entries during the update. +var version = 'v1::'; + +// The install event fires when the service worker is first installed. +// You can use this event to prepare the service worker to be able to serve +// files while visitors are offline. + +wrk.addEventListener("install", function (event: any) { + console.info('[OfflineWorker] : install event in progress.'); + + // get the path for the .json file which contains the files to-be-cached + // These resources will be downloaded and cached by the service worker + // If any resource fails to be downloaded, then the service worker won't be installed. + var jsonPath = new URL(location.href).searchParams.get('jsonPath'); + + // Using event.waitUntil(p) blocks the installation process on the provided + // promise. If the promise is rejected, the service worker won't be installed. + event.waitUntil( + // let's request the files to store in cache + fetch(jsonPath) + .then(response => response.json()) + // after we received the files to store, we open the cache + .then(data => caches.open(version + 'fundamentals') + // then map the array and add one-by-one + .then(cache => data.map((url) => cache.add(url)))) + // log success + .then(() => console.info('[OfflineWorker] : install completed')) + ); +}); + +// The fetch event fires whenever a page controlled by this service worker requests +// a resource. This isn't limited to `fetch` or even XMLHttpRequest. Instead, it +// comprehends even the request for the HTML page on first load, as well as JS and +// CSS resources, fonts, any images, etc. +wrk.addEventListener("fetch", function (event: any) { + console.info('[OfflineWorker] : fetch event in progress.'); + if (event.request.method !== 'GET') { + console.info('[OfflineWorker] : fetch event ignored.', event.request.method, event.request.url); + return; + } + + // This method returns a promise that resolves to a cache entry matching + // the request. Once the promise is settled, we can then provide a response + // to the fetch request. + event.respondWith( + caches.match(event.request) + .then(async (cached) => { + // return immediately if cache successfull + if (cached) { + console.info('[OfflineWorker] : fetch event(cached): ', event.request.url); + return cached + } + + // Else, use the preloaded response, if it's there + const preload = await event.preloadResponse; + if (preload) return preload; + + // We copy the response before replying to the network request. + // This is the response that will be stored on the ServiceWorker cache. + function fetchedFromNetwork(response) { + var cacheCopy = response.clone(); + console.info('[OfflineWorker] : fetch response from network.', event.request.url); + // We open a cache to store the response for this request. + caches.open(version + 'pages') + .then((cache) => cache.put(event.request, cacheCopy)) + .then(() => console.info('[OfflineWorker] : fetch response stored in cache.', event.request.url)); + // Return the response so that the promise is settled in fulfillment. + return response; + } + + /* When this method is called, it means we were unable to produce a response + from either the cache or the network. This is our last opportunity + to produce a meaningful response even when all else fails. + E.g. + - Test the Accept header and then return one of the `offlineFundamentals` + e.g: `return caches.match('/some/cached/image.png')` + - consider the origin. It's easier to decide what "unavailable" means + for requests against your origins than for requests against a third party, + such as an ad provider. + - Generate a Response programmaticaly, as shown below, and return that. + */ + function unableToResolve() { + console.info('[OfflineWorker] : fetch request failed in both cache and network.'); + return new Response('Service Unavailable', { + status: 503, + statusText: 'Service Unavailable', + headers: new Headers({ + 'Content-Type': 'text/html' + }) + }); + } + + // fallback to fetching from network + return fetch(event.request) + // We handle the network request with success and failure scenarios. + .then(fetchedFromNetwork, unableToResolve) + // we are done + .then(() => console.info('[OfflineWorker] : fetch event(networked): ', event.request.url)) + // We should catch errors on the fetchedFromNetwork handler as well. + .catch(unableToResolve); + }) + ); +}); + +/* The activate event fires after a service worker has been successfully installed. + It is most useful when phasing out an older version of a service worker, as at + this point you know that the new worker was installed correctly. In this example, + we delete old caches that don't match the version in the worker we just finished + installing. +*/ +wrk.addEventListener("activate", function (event: any) { + console.info('[OfflineWorker] : activate event in progress.'); + event.waitUntil(async () => { + // Feature-detect navigation preloads! + if (self['registration'] && self['registration'].navigationPreload) { + await self['registration'].navigationPreload.enable(); + } + + // This method will resolve an array of available cache keys. + caches.keys() + // We return a promise that settles when all outdated caches are deleted. + .then(keys => Promise.all( + // Filter by keys that don't start with the latest version prefix. + keys.filter((key) => !key.startsWith(version)) + // Return a promise that's fulfilled when each outdated cache is deleted. + .map((key) => caches.delete(key)))) + // completed + .then(() => console.info('[OfflineWorker] : activate completed.')); + }); + + // Tell the active service worker to take control of the page immediately. + if (wrk['clients']) wrk['clients'].claim(); +}); \ No newline at end of file diff --git a/src/WEWWA.ts b/src/WEWWA.ts new file mode 100644 index 0000000..12f62bf --- /dev/null +++ b/src/WEWWA.ts @@ -0,0 +1,755 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @see + * REQUIREMENTS: + * - JQUERY >= 3.3.1 + * - HTML5 supported Browser (for webAudio processing) + * - this file needs to be in the same (root) folder as your "project.json" + * - this file needs to be included/loaded in your "index.html" + * + * @description + * WEWWA + * Wallpaper Engine Web Wallpaper Adapter + * + * This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine + * Web-Wallpaper project - so you can test, run & configure it from a normal web browser. + * + * FEATURES: + * - automatically detecting if the web wallpaper is opened by wallpaper engine or browser + * - if opened by wallpaper engine, nothing will happen + * - if opened by a browser: + * - automatically load the "project.json" + * - parse the settings, languages & conditions + * - add respective html elements for each setting type & condition + * - put these elements into an option menu which can be hidden + * - check localStorage for already saved/customized values + * - apply all settings once + * - react to changes made in the ui and update them in the wallpaper + * - save changes made in the ui to localStorage + * +*/ + +import { Ready } from "./Ready"; +import { Smallog } from "./Smallog"; +import { OfflineHelper } from "./OfflineHelper"; + +export class WEWWA { + + private project: any = null; + + private htmlIcon: Element = null; + private htmlMenu: Element = null; + + private audio: any = null; + private ctx: any = null; + private source: any = null; + private analyser: any = null; + + private audioInterval: any = null; + private audioCallback: any = null; + + + constructor() { + if (window['wallpaperRegisterAudioListener']) Smallog.Info("[WEWWA] detected wallpaper engine => Standby."); + else { + Smallog.Info("[WEWWA] wallpaper engine not detected => Init!"); + // define audio listener first, so we dont miss when it gets registered. + window['wallpaperRegisterAudioListener'] = function (callback) { + // set callback to be called later with analysed audio data + this.audioCallback = callback; + } + // intialize when ready + Ready.On(() => { + // make the website available offline using service worker + OfflineHelper.Register(); + // continue initializing + this.Init(); + }); + } + } + + Init = () => { + this.LoadProjectJSON((proj) => { + if (proj.type != "web") { + Smallog.Error("Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting..."); + return; + } + this.project = proj; + this.LoadStorage(); + this.AddStyle(); + this.AddMenu(localStorage.getItem("wewwaLang")); + this.UpdateSettings(); + this.ApplyProp(proj.general.properties); + }); + } + + LoadProjectJSON = (complete) => { + $.ajax({ + url: "project.json", + beforeSend: (xhr) => xhr.overrideMimeType("text/plain;"), + success: (result) => complete(JSON.parse(result)), + error: (xhr, status, error) => Smallog.Error(status + ": ajax error!\r\n" + error) + }); + } + + LoadStorage = () => { + var props = this.project.general.properties; + var last = localStorage.getItem("wewwaLastProps"); + if (last != null) { + var merged = Object.assign(props, JSON.parse(last)); + this.project.general.properties = merged; + Smallog.Debug("Loaded & merged settings.") + } + } + + AddStyle = () => { + var st = document.createElement("style"); + st.innerHTML = ` + .wewwaMenu, .wewwaIcon { + transform: none; + transition: transform 500ms ease; + position:absolute; + top:0px; + padding:10px; + margin:10px; + z-index:9999; + } + .wewwaMenu { + border: solid 2px #444; + width:400px; + right:-440px; + color:white; + background-color:#333333; + overflow-x:hidden; + overflow-y:scroll; + max-height:95%; + max-width: 90%; + font-family: Helvetica, Verdana, Arial; + font-size: larger; + } + .wewwaMenu.open { + transform: translateX(-440px); + transition: transform 500ms ease; + } + @media all and (max-width: 520px) { + .wewwaMenu.open { + max-height:85%; + transform: translateX(-440px) translateY(55px); + transition: transform 500ms ease; + } + } + .wewwaMenu a { + color: white; + border: 2px solid #4CAF50; + padding: 5px 20px; + text-decoration: none; + display: inline-block; + } + .wewwaMenu a:hover { + background: #4CAF50; + } + .wewwaMenu audio { + width: 100%; + } + .wewwaMenu table { + width:100%; + table-layout: fixed; + } + .wewwaMenu td { + width: 50%; + padding: 5px; + } + .wewwaMenu img { + width: 200px; + max-width: 90%; + heigth: auto; + } + .wewwaMenu input[type='checkbox'][readonly]{ + pointer-events: none; + } + .wewwaMenu .droparea { + border: 2px dashed #bbb; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 20px; + text-align: center; + font: 18pt; + color: #bbb; + } + .wewwaIcon { + right:0px; + cursor:pointer; + } + .wewwaIcon div { + width:35px; + height:5px; + background-color:#888888; + margin:6px 0; + } + @media all and (min-width: 520px) { + .wewwaIcon.open { + transform: translateX(-440px); + transition: transform 500ms ease; + } + } + `; + document.head.append(st); + } + + AddMenu = (lang) => { + if (this.htmlMenu) { + document.body.removeChild(this.htmlMenu); + document.body.removeChild(this.htmlIcon); + this.htmlMenu = null; + } + // quick wrapper, we need this a lot + var ce = (e) => document.createElement(e); + // local vars faster + var proj = this.project; + var props = proj.general.properties; + + // create root menu + var menu = ce("div"); + menu.classList.add("wewwaMenu"); + // create preview img wrap + var preview = ce("img"); + preview.setAttribute("src", proj.preview); + // create menu app title + var header = ce("div"); + header.innerHTML = "

" + proj.title + "

"; + // create workshop link + var link = ce("a"); + link.setAttribute("href", "https://steamcommunity.com/sharedfiles/filedetails/?id=" + proj.workshopid); + link.setAttribute("target", "_blank"); + link.innerHTML = "

Open Workshop Page

"; + + // table is better for formatting + var tmain = ce("table") + tmain.innerHTML = " "; + var table = ce("tbody"); + tmain.append(table); + + // if app supports audio, add input menu & handlers + if (proj.general.supportsaudioprocessing) { + + // audio input methods + var row = ce("tr"); + var td1 = ce("td"); + td1.innerHTML = "

Audio Input


"; + td1.setAttribute("colspan", 3); + var aBtn1 = ce("a"); + var aBtn2 = ce("a"); + var aBtn3 = ce("input"); + aBtn1.innerHTML = "Microphone"; + aBtn1.addEventListener("click", function (e) { + this.requestMicrophone(); + }); + aBtn2.innerHTML = "Select URL"; + aBtn2.addEventListener("click", function (e) { + var uri = prompt("Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!", "https://example.com/test.mp3"); + this.initiateAudio(uri); + }); + aBtn3.id = "wewwaAudioInput"; + aBtn3.innerHTML = "Select File"; + aBtn3.setAttribute("type", "file"); + aBtn3.addEventListener("change", function (e) { + var file = e.target.files[0]; + if (!file) return; + this.initiateAudio(file); + }); + td1.append(aBtn1, aBtn2, aBtn3); + row.append(td1); + + // file drag & drop area + var dropRow = ce("tr"); + var dropt1 = ce("td"); + var dropt2 = ce("td"); + dropt1.setAttribute("colspan", 3); + var dropArea = ce("div"); + dropArea.innerHTML = "Drag & Drop" + dropArea.classList.add("droparea"); + dropArea.addEventListener('dragover', (evt) => { + evt.stopPropagation(); + evt.preventDefault(); + evt.dataTransfer.dropEffect = 'copy'; + }, false); + dropArea.addEventListener("drop", (e) => { + e.stopPropagation(); + e.preventDefault(); + var droppedFiles = e.dataTransfer.files; + this.initiateAudio(droppedFiles[0]); + }, false); + dropt1.append(dropArea); + dropRow.append(dropt1, dropt2); + + // Player & Stop Btn + var hrrow = ce("tr"); + var hrtd1 = ce("td"); + var hrtd2 = ce("td"); + var hrstop = ce("a"); + hrstop.innerHTML = "Stop All Audio"; + hrstop.addEventListener("click", function (e) { + this.stopAudioInterval(); + }); + var hrhr = ce("hr") + hrtd1.id = "audioMarker"; + hrtd1.setAttribute("colspan", 3); + hrtd1.append(hrstop, hrhr); + hrrow.append(hrtd1, hrtd2); + + // finally add rows to table + table.append(row, dropRow, hrrow); + } + + // create actual settings wrapper + var settings = ce("tr"); + settings.innerHTML = "

Settings

"; + table.append(settings); + + // process languages? + var local = proj.general.localization; + if (local) { + // set default language + if (!lang) lang = "en-us"; + // add html struct + var row = ce("tr"); + var td1 = ce("td"); + td1.innerHTML = "

🇩🇪🇬🇧🇮🇹🇷🇺🇨🇳

"; + var td2 = ce("td"); + var lan = ce("select"); + // process all + for (var loc in local) { + // build select option for this + var lcs = ce("option"); + lcs.value = loc; + lcs.innerHTML = loc.toUpperCase(); + lan.append(lcs); + // check for correct language code + if (loc != lang) continue; + else lcs.setAttribute("selected", true); + // set properties translated text + for (var p in props) { + var itm = props[p]; + var pTxt = itm.text; + var rTxt = local[loc][pTxt]; + if (rTxt) itm.realText = rTxt; + // process combo box values + if (itm.type == "combo") { + for (var o of itm.options) { + var lTxt = local[loc][o.label]; + if (lTxt) o.realLabel = lTxt; + } + } + } + } + // if changed, do it all over again. + lan.addEventListener("change", function (e) { + localStorage.setItem("wewwaLang", this.value); + this.AddMenu(this.value); + this.UpdateSettings(); + this.html.icon.click(); + }); + td2.setAttribute("colspan", 2); + td2.append(lan); + row.append(td1, td2); + table.append(row); + } + + // split content from actual settings + var splitr = ce("tr"); + splitr.innerHTML = "
"; + table.append(splitr); + + // sort settings by order + var sortable = []; + for (var p in props) sortable.push([p, props[p]]); + sortable.sort((a, b) => a[1].order - b[1].order); + // add setting html elements + for (var s of sortable) + table.append(this.CreateItem(s[0], s[1])); + + // pre-footer for resetting saved settings + var preFoot = ce("div"); + preFoot.innerHTML = "

"; + var reset = ce("a"); + reset.innerHTML = "Reset Settings"; + reset.addEventListener("click", function (e) { + localStorage.clear(); + location = location; + }); + preFoot.append(reset); + + // footer with ident + var footer = ce("div"); + footer.innerHTML = "

[W] allpaper
[E] ngine
[W] eb
[W] allpaper
[A] dapterhexxone"; + // finish up menu + menu.append(preview, header, link, tmain, preFoot, footer) + + // create icon for opening & closing the menu + var icon = ce("div"); + icon.classList.add("wewwaIcon"); + icon.addEventListener("click", () => { + $(".wewwaMenu, .wewwaIcon").toggleClass("open"); + }); + var bar1 = ce("div"); + var bar2 = ce("div"); + var bar3 = ce("div"); + icon.append(bar1, bar2, bar3); + + // finally add the menu to the DOM + document.body.append(menu, icon); + this.htmlMenu = menu; + this.htmlIcon = icon; + } + + CreateItem = (prop, itm) => { + var ce = (e) => document.createElement(e); + var row = ce("tr"); + row.setAttribute("id", "wewwa_" + prop); + var td1 = ce("td"); + var td2 = ce("td"); + var td3 = null; + var txt = ce("div"); + txt.innerHTML = itm.realText ? itm.realText : itm.text; + // create real input element + var inp = ce("input"); + inp.setAttribute("id", "wewwa_inp_" + prop); + switch (itm.type) { + // only have text + case "text": + inp = null; + td1.setAttribute("colspan", 3); + break; + // add combo select options + case "combo": + inp = ce("select"); + // set options + for (var o of itm.options) { + var opt = ce("option"); + opt.setAttribute("value", o.value); + opt.innerText = o.realLabel ? o.realLabel : o.label; + if (itm.value == o.value) opt.setAttribute("selected", true); + inp.appendChild(opt); + } + break; + // browser color picker + case "color": + inp.setAttribute("type", "color"); + break; + // Checkbox + case "bool": + inp.setAttribute("type", "checkbox"); + inp.setAttribute("readonly", true); + // makes ticking checkboxes easier + row.addEventListener("click", (e) => { + inp.click(); + }); + break; + // Slider input + case "slider": + var canEdit = itm.editable; + // create numeric-up-down + var sliderVal = ce(canEdit ? "input" : "output"); + sliderVal.name = "wewwa_out_" + prop; + sliderVal.setAttribute("id", sliderVal.name); + sliderVal.setAttribute("type", "number"); + sliderVal.style.width = "75%"; + if (canEdit) { + sliderVal.setAttribute("value", itm.value); + sliderVal.addEventListener("change", function (e) { this.SetProperty(prop, this); }); + } + else { + sliderVal.innerHTML = itm.value; + } + // create td3 + td3 = ce("td"); + td3.append(sliderVal); + // create actual slider & values + inp.setAttribute("type", "range"); + inp.style.width = "100%"; + inp.max = itm.max; + inp.min = itm.min; + inp.step = 0.1; + break; + case "textinput": + inp.setAttribute("type", "text"); + break; + case "file": + inp.setAttribute("type", "file"); + break; + default: + Smallog.Error("unkown setting type: " + itm.type); + break; + } + td1.append(txt); + // listen for changes if input type (no text) + if (inp) { + inp.addEventListener("change", function (e) { this.SetProperty(prop, this); }); + td2.prepend(inp); + } + // append td3 or stretch td2? + if (td3) { + row.append(td1, td2, td3) + } + else { + td2.setAttribute("colspan", 2); + row.append(td1, td2); + } + return row; + } + + + SetProperty = (prop, elm) => { + // get the type and apply the value + var props = this.project.general.properties; + // check for legit setting... + if (!props[prop]) { + Smallog.Error("SetProperty name not found!"); + return; + } + // enabled delayed call of settings update + var applyCall = (val) => { + // save the updated value to storage + props[prop].value = val; + + //Smallog.Debug("Property set: " + prop + " v: " + val); + + // update + this.UpdateSettings(); + var obj = {}; + obj[prop] = props[prop]; + this.ApplyProp(obj); + }; + // process value based on DOM element type + switch (props[prop].type) { + case "bool": + applyCall(elm.checked == true); + break; + case "color": + applyCall(this.hexToRgb(elm.value)); + break; + case "file": + this.XHRLoadAndSaveLocal(elm.value, res => applyCall(res)); + break; + case "slider": + if (elm.name.includes("_out_")) { + var inpt: any = document.querySelector("#wewwa_" + prop); + if (inpt) inpt.value = elm.value; + else Smallog.Error("Slider not found: " + prop); + } + else { + var slide: any = document.querySelector("#wewwa_out_" + prop); + if (slide) slide.value = elm.value; + else Smallog.Error("Numericupdown not found: " + prop); + } + case "combo": + case "textinput": + applyCall(elm.value); + break; + } + } + + + // will load the given file and return it as dataURL. + // this way we can easily store whole files in the configuration/localStorage. + // its not safe that this works with something else than image files. + XHRLoadAndSaveLocal = (url, resCall) => { + // Create XHR and FileReader objects + var xhr = new XMLHttpRequest(); + var fileReader = new FileReader(); + xhr.open("GET", url, true); + xhr.responseType = "blob"; + xhr.addEventListener("load", function () { + if (xhr.status == 200) { + // onload needed since Google Chrome doesn't support addEventListener for FileReader + fileReader.onload = function (evt) { + // Read out file contents as a Data URL + resCall(evt.target.result) + }; + // Load blob as Data URL + fileReader.readAsDataURL(xhr.response); + } + }, false); + // Send XHR + xhr.send(); + } + + + UpdateSettings = () => { + var wewwaProps = this.project.general.properties; + localStorage.setItem("wewwaLastProps", JSON.stringify(wewwaProps)); + for (var p in wewwaProps) { + var prop = wewwaProps[p]; + + // some eval magic + var visible = true; + if (prop.condition != null) { + // copy our condition string to modify + var cprop = String(prop.condition).split(" ").join(""); + // remove whitespaces and split to partials by logic operators + var partials = cprop.split(/&&|\|\|/); + // loop all partial values of the check + for (var part of partials) { + var prefix = "wewwaProps."; + var onlyVal = part.match(/[!a-zA-Z0-9_\.]*/)[0]; + if (!onlyVal.startsWith(prefix) && !onlyVal.startsWith("!" + prefix)) { + // fix for inverted values + var replW = onlyVal; + if (replW.startsWith("!")) { + replW = replW.substr(1); + prefix = "!" + prefix; + } + //Smallog.Debug("replace: " + onlyVal + " >> " + prefix + replW); + cprop = cprop.replace(onlyVal, prefix + replW); + } + + } + try { + visible = eval(cprop) == true; + } + catch (e) { + Smallog.Error("Error: (" + cprop + ") for: " + p + " => " + e); + } + } + + + if (visible) $("#wewwa_" + p).fadeIn(); + else $("#wewwa_" + p).fadeOut(); + + // get input dom element + var elm: any = document.getElementById("wewwa_" + p).childNodes[1].childNodes[0]; + switch (prop.type) { + case "color": + elm.value = this.rgbToHex(prop.value); + break; + case "bool": + elm.checked = prop.value == true; + break; + case "slider": + case "combo": + case "textinput": + elm.value = prop.value; + break; + } + } + } + + + ApplyProp = (prop) => { + var wpl = window['wallpaperPropertyListener']; + if (wpl && wpl.applyUserProperties) { + wpl.applyUserProperties(prop); + } + } + + rgbToHex = (rgb) => { + function cth(c) { + var h = Math.floor(c * 255).toString(16); + return h.length == 1 ? "0" + h : h; + } + var spl = rgb.split(" "); + return "#" + cth(spl[0]) + cth(spl[1]) + cth(spl[2]); + } + + hexToRgb = (hex) => { + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255].join(" ") : null; + } + + requestMicrophone = () => { + navigator.mediaDevices.getUserMedia({ + audio: true + }).then(function (stream) { + this.stopAudioInterval(); + // hack for firefox to keep stream running + window['persistAudioStream'] = stream; + this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); + this.source = this.ctx.createMediaStreamSource(stream); + this.analyser = this.ctx.createAnalyser(); + this.analyser.smoothingTimeConstant = 0.35; + this.analyser.fftSize = 256; + + this.source.connect(this.analyser); + this.startAudioInterval(); + }).catch(function (err) { + Smallog.Error(err); + if (location.protocol != "https:") { + var r = confirm("Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS for this feature to work! Press 'ok'/'yes' to get redirected to HTTPS and try again."); + if (r) window.location.href = window.location.href.replace("http", "https"); + } + }); + } + + // html5 audio analyser gives us mono data from 0(bass) to 128(treble) + // however, wallpaper engine expects stereo data in following format: + // 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) + // so we do some array transformation... and divide by 255 (8bit-uint becomes float) + convertAudio = (data) => { + var stereo = []; + var sIdx = 0; + for (var i = 0; i < 64; i++) { + stereo[i] = data[sIdx++] / 255; + stereo[127 - i] = data[sIdx++] / 255; + } + return stereo; + } + + + initiateAudio = (data) => { + // clear up + this.stopAudioInterval(); + // create player + this.audio = document.createElement("audio"); + this.audio.src = data.name ? URL.createObjectURL(data) : data; + this.audio.autoplay = true; + this.audio.setAttribute("controls", "true"); + this.audio.play = true; + + $("#audioMarker").prepend(this.audio); + + this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); + this.source = this.ctx.createMediaElementSource(this.audio); + this.analyser = this.ctx.createAnalyser(); + this.analyser.smoothingTimeConstant = 0.35; + this.analyser.fftSize = 256; + + this.source.connect(this.ctx.destination); + this.source.connect(this.analyser); + this.startAudioInterval(); + } + + + startAudioInterval = () => { + var data = new Uint8Array(128); + // 33ms ~~ 30fps + this.audioInterval = setInterval(() => { + if (this.audioCallback != null) { + this.stopAudioInterval(); + alert("no AudioCallback!"); + return; + } + this.analyser.getByteFrequencyData(data); + var stereo = this.convertAudio(data); + this.audioCallback(stereo); + }, 33); + // tell Wallpaper we are sending audio + this.ApplyProp({ audioprocessing: { value: true } }) + } + + stopAudioInterval = () => { + window['persistAudioStream'] = null; + $("#wewwaAudioInput").val(""); + if (this.audio) + this.audio.remove(); + if (this.audioInterval) { + clearInterval(this.audioInterval); + this.audioInterval = null; + } + } +} diff --git a/wewwa.js b/wewwa.js deleted file mode 100644 index 4d4c0c4..0000000 --- a/wewwa.js +++ /dev/null @@ -1,750 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @see - * REQUIREMENTS: - * - JQUERY >= 3.3.1 - * - HTML5 supported Browser (for webAudio processing) - * - this file needs to be in the same (root) folder as your "project.json" - * - this file needs to be included/loaded in your "index.html" - * - * @description - * WEWWA - * Wallpaper Engine Web Wallpaper Adapter - * - * This is an aditional JS file to be included in any Wallpaper Engine - * Web-Wallpaper project so you can test, run & configure it from a normal web browser. - * - * FEATURES: - * - automatically detecting if the web wallpaper is opened by wallpaper engine or browser - * - if opened by wallpaper engine, nothing will happen - * - if opened by a browser: - * - automatically load the "project.json" - * - parse the settings, languages & conditions - * - add respective html elements for each setting type & condition - * - put these elements into an option menu which can be hidden - * - check localStorage for already saved/customized values - * - apply all settings once - * - react to changes made in the ui and update them in the wallpaper - * - save changes made in the ui to localStorage - * -*/ - -var wewwApp = wewwApp || {}; - -wewwApp.Init = () => { - wewwApp.LoadProjectJSON((proj) => { - if (proj.type != "web") { - console.error("Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting..."); - return; - } - wewwApp.project = proj; - wewwApp.LoadStorage(); - wewwApp.AddStyle(); - wewwApp.AddMenu(localStorage.getItem("wewwaLang")); - wewwApp.UpdateSettings(); - wewwApp.ApplyProp(proj.general.properties); - }); -} - - -// load json via ajax request -wewwApp.LoadProjectJSON = (complete) => { - $.ajax({ - url: "project.json", - beforeSend: (xhr) => xhr.overrideMimeType("text/plain;"), - success: (result) => complete(JSON.parse(result)), - error: (xhr, status, error) => console.error(status + ": ajax error!\r\n" + error) - }); -} - -wewwApp.LoadStorage = () => { - var props = wewwApp.project.general.properties; - var last = localStorage.getItem("wewwaLastProps"); - if (last != null) { - var merged = Object.assign(props, JSON.parse(last)); - wewwApp.project.general.properties = merged; - console.debug("Loaded & merged settings.") - } -} - -// add style after loading page -wewwApp.AddStyle = () => { - var st = document.createElement("style"); - st.innerHTML = ` - .wewwaMenu, .wewwaIcon { - transform: none; - transition: transform 500ms ease; - position:absolute; - top:0px; - padding:10px; - margin:10px; - z-index:9999; - } - .wewwaMenu { - border: solid 2px #444; - width:400px; - right:-440px; - color:white; - background-color:#333333; - overflow-x:hidden; - overflow-y:scroll; - max-height:95%; - max-width: 90%; - font-family: Helvetica, Verdana, Arial; - font-size: larger; - } - .wewwaMenu.open { - transform: translateX(-440px); - transition: transform 500ms ease; - } - @media all and (max-width: 520px) { - .wewwaMenu.open { - max-height:85%; - transform: translateX(-440px) translateY(55px); - transition: transform 500ms ease; - } - } - .wewwaMenu a { - color: white; - border: 2px solid #4CAF50; - padding: 5px 20px; - text-decoration: none; - display: inline-block; - } - .wewwaMenu a:hover { - background: #4CAF50; - } - .wewwaMenu audio { - width: 100%; - } - .wewwaMenu table { - width:100%; - table-layout: fixed; - } - .wewwaMenu td { - width: 50%; - padding: 5px; - } - .wewwaMenu img { - width: 200px; - max-width: 90%; - heigth: auto; - } - .wewwaMenu input[type='checkbox'][readonly]{ - pointer-events: none; - } - .wewwaMenu .droparea { - border: 2px dashed #bbb; - -webkit-border-radius: 5px; - border-radius: 5px; - padding: 20px; - text-align: center; - font: 18pt; - color: #bbb; - } - .wewwaIcon { - right:0px; - cursor:pointer; - } - .wewwaIcon div { - width:35px; - height:5px; - background-color:#888888; - margin:6px 0; - } - @media all and (min-width: 520px) { - .wewwaIcon.open { - transform: translateX(-440px); - transition: transform 500ms ease; - } - } - `; - document.head.append(st); -} - -// create menu after loading page -wewwApp.AddMenu = (lang) => { - if (wewwApp.html) { - document.body.removeChild(wewwApp.html.menu); - document.body.removeChild(wewwApp.html.icon); - wewwApp.html = null; - } - // quick wrapper, we need this a lot - var ce = (e) => document.createElement(e); - // local vars faster - var proj = wewwApp.project; - var props = proj.general.properties; - - // create root menu - var menu = ce("div"); - menu.classList.add("wewwaMenu"); - // create preview img wrap - var preview = ce("img"); - preview.setAttribute("src", proj.preview); - // create menu app title - var header = ce("div"); - header.innerHTML = "

" + proj.title + "

"; - // create workshop link - var link = ce("a"); - link.setAttribute("href", "https://steamcommunity.com/sharedfiles/filedetails/?id=" + proj.workshopid); - link.setAttribute("target", "_blank"); - link.innerHTML = "

Open Workshop Page

"; - - // table is better for formatting - var tmain = ce("table") - tmain.innerHTML = " "; - var table = ce("tbody"); - tmain.append(table); - - // if app supports audio, add input menu & handlers - if (proj.general.supportsaudioprocessing) { - - // audio input methods - var row = ce("tr"); - var td1 = ce("td"); - td1.innerHTML = "

Audio Input


"; - td1.setAttribute("colspan", 3); - var aBtn1 = ce("a"); - var aBtn2 = ce("a"); - var aBtn3 = ce("input"); - aBtn1.innerHTML = "Microphone"; - aBtn1.addEventListener("click", function (e) { - wewwApp.requestMicrophone(); - }); - aBtn2.innerHTML = "Select URL"; - aBtn2.addEventListener("click", function (e) { - var uri = prompt("Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!", "https://example.com/test.mp3"); - wewwApp.initiateAudio(uri); - }); - aBtn3.id = "wewwaAudioInput"; - aBtn3.innerHTML = "Select File"; - aBtn3.setAttribute("type", "file"); - aBtn3.addEventListener("change", function (e) { - var file = e.target.files[0]; - if (!file) return; - wewwApp.initiateAudio(file); - }); - td1.append(aBtn1, aBtn2, aBtn3); - row.append(td1); - - // file drag & drop area - var dropRow = ce("tr"); - var dropt1 = ce("td"); - var dropt2 = ce("td"); - dropt1.setAttribute("colspan", 3); - var dropArea = ce("div"); - dropArea.innerHTML = "Drag & Drop" - dropArea.classList.add("droparea"); - dropArea.addEventListener('dragover', (evt) => { - evt.stopPropagation(); - evt.preventDefault(); - evt.dataTransfer.dropEffect = 'copy'; - }, false); - dropArea.addEventListener("drop", (e) => { - e.stopPropagation(); - e.preventDefault(); - var droppedFiles = e.dataTransfer.files; - wewwApp.initiateAudio(droppedFiles[0]); - }, false); - dropt1.append(dropArea); - dropRow.append(dropt1, dropt2); - - // Player & Stop Btn - var hrrow = ce("tr"); - var hrtd1 = ce("td"); - var hrtd2 = ce("td"); - var hrstop = ce("a"); - hrstop.innerHTML = "Stop All Audio"; - hrstop.addEventListener("click", function (e) { - wewwApp.stopAudioInterval(); - }); - var hrhr = ce("hr") - hrtd1.id = "audioMarker"; - hrtd1.setAttribute("colspan", 3); - hrtd1.append(hrstop, hrhr); - hrrow.append(hrtd1, hrtd2); - - // finally add rows to table - table.append(row, dropRow, hrrow); - } - - // create actual settings wrapper - var settings = ce("tr"); - settings.innerHTML = "

Settings

"; - table.append(settings); - - // process languages? - var local = proj.general.localization; - if (local) { - // set default language - if (!lang) lang = "en-us"; - // add html struct - var row = ce("tr"); - var td1 = ce("td"); - td1.innerHTML = "

🇩🇪🇬🇧🇮🇹🇷🇺🇨🇳

"; - var td2 = ce("td"); - var lan = ce("select"); - // process all - for (var loc in local) { - // build select option for this - var lcs = ce("option"); - lcs.value = loc; - lcs.innerHTML = loc.toUpperCase(); - lan.append(lcs); - // check for correct language code - if (loc != lang) continue; - else lcs.setAttribute("selected", true); - // set properties translated text - for (var p in props) { - var itm = props[p]; - var pTxt = itm.text; - var rTxt = local[loc][pTxt]; - if (rTxt) itm.realText = rTxt; - // process combo box values - if (itm.type == "combo") { - for (var o of itm.options) { - var lTxt = local[loc][o.label]; - if (lTxt) o.realLabel = lTxt; - } - } - } - } - // if changed, do it all over again. - lan.addEventListener("change", function (e) { - localStorage.setItem("wewwaLang", this.value); - wewwApp.AddMenu(this.value); - wewwApp.UpdateSettings(); - wewwApp.html.icon.click(); - }); - td2.setAttribute("colspan", 2); - td2.append(lan); - row.append(td1, td2); - table.append(row); - } - - // split content from actual settings - var splitr = ce("tr"); - splitr.innerHTML = "
"; - table.append(splitr); - - // sort settings by order - var sortable = []; - for (var p in props) sortable.push([p, props[p]]); - sortable.sort((a, b) => a[1].order - b[1].order); - // add setting html elements - for (var s of sortable) - table.append(wewwApp.CreateItem(s[0], s[1])); - - // pre-footer for resetting saved settings - var preFoot = ce("div"); - preFoot.innerHTML = "

"; - var reset = ce("a"); - reset.innerHTML = "Reset Settings"; - reset.addEventListener("click", function (e) { - localStorage.clear(); - location = location; - }); - preFoot.append(reset); - - // footer with ident - var footer = ce("div"); - footer.innerHTML = "

[W] allpaper
[E] ngine
[W] eb
[W] allpaper
[A] dapterhexxone"; - // finish up menu - menu.append(preview, header, link, tmain, preFoot, footer) - - // create icon for opening & closing the menu - var icon = ce("div"); - icon.classList.add("wewwaIcon"); - icon.addEventListener("click", () => { - $(".wewwaMenu, .wewwaIcon").toggleClass("open"); - }); - var bar1 = ce("div"); - var bar2 = ce("div"); - var bar3 = ce("div"); - icon.append(bar1, bar2, bar3); - - // finally add the menu to the DOM - document.body.append(menu, icon); - wewwApp.html = { - menu: menu, - icon: icon - }; -} - -// create html elements & tr for a settings item -wewwApp.CreateItem = (prop, itm) => { - var ce = (e) => document.createElement(e); - var row = ce("tr"); - row.setAttribute("id", "wewwa_" + prop); - var td1 = ce("td"); - var td2 = ce("td"); - var td3 = null; - var txt = ce("div"); - txt.innerHTML = itm.realText ? itm.realText : itm.text; - // create real input element - var inp = ce("input"); - inp.setAttribute("id", "wewwa_inp_" + prop); - switch (itm.type) { - // only have text - case "text": - inp = null; - td1.setAttribute("colspan", 3); - break; - // add combo select options - case "combo": - inp = ce("select"); - // set options - for (var o of itm.options) { - var opt = ce("option"); - opt.setAttribute("value", o.value); - opt.innerText = o.realLabel ? o.realLabel : o.label; - if (itm.value == o.value) opt.setAttribute("selected", true); - inp.appendChild(opt); - } - break; - // browser color picker - case "color": - inp.setAttribute("type", "color"); - break; - // Checkbox - case "bool": - inp.setAttribute("type", "checkbox"); - inp.setAttribute("readonly", true); - // makes ticking checkboxes easier - row.addEventListener("click", (e) => { - inp.click(); - }); - break; - // Slider input - case "slider": - var canEdit = itm.editable; - // create numeric-up-down - var sliderVal = ce(canEdit ? "input" : "output"); - sliderVal.name = "wewwa_out_" + prop; - sliderVal.setAttribute("id", sliderVal.name); - sliderVal.setAttribute("type", "number"); - sliderVal.style.width = "75%"; - if (canEdit) { - sliderVal.setAttribute("value", itm.value); - sliderVal.addEventListener("change", function (e) { wewwApp.SetProperty(prop, this); }); - } - else { - sliderVal.innerHTML = itm.value; - } - // create td3 - td3 = ce("td"); - td3.append(sliderVal); - // create actual slider & values - inp.setAttribute("type", "range"); - inp.style.width = "100%"; - inp.max = itm.max; - inp.min = itm.min; - inp.step = 0.1; - break; - case "textinput": - inp.setAttribute("type", "text"); - break; - case "file": - inp.setAttribute("type", "file"); - break; - default: - console.error("unkown setting type: " + itm.type); - break; - } - td1.append(txt); - // listen for changes if input type (no text) - if (inp) { - inp.addEventListener("change", function (e) { wewwApp.SetProperty(prop, this); }); - td2.prepend(inp); - } - // append td3 or stretch td2? - if (td3) { - row.append(td1, td2, td3) - } - else { - td2.setAttribute("colspan", 2); - row.append(td1, td2); - } - return row; -} - -// apply html value/setting to object -wewwApp.SetProperty = (prop, elm) => { - // get the type and apply the value - var props = wewwApp.project.general.properties; - // check for legit setting... - if (!props[prop]) { - console.error("SetProperty name not found!"); - return; - } - // enabled delayed call of settings update - var applyCall = (val) => { - // save the updated value to storage - props[prop].value = val; - - //console.debug("Property set: " + prop + " v: " + val); - - // update - wewwApp.UpdateSettings(); - var obj = {}; - obj[prop] = props[prop]; - wewwApp.ApplyProp(obj); - }; - // process value based on DOM element type - switch (props[prop].type) { - case "bool": - applyCall(elm.checked == true); - break; - case "color": - applyCall(wewwApp.hexToRgb(elm.value)); - break; - case "file": - wewwApp.XHRLoadAndSaveLocal(elm.value, res => applyCall(res)); - break; - case "slider": - if (elm.name.includes("_out_")) { - var inpt = document.querySelector("#wewwa_" + prop); - if (inpt) inpt.value = elm.value; - else console.error("Slider not found: " + prop); - } - else { - var slide = document.querySelector("#wewwa_out_" + prop); - if (slide) slide.value = elm.value; - else console.error("Numericupdown not found: " + prop); - } - case "combo": - case "textinput": - applyCall(elm.value); - break; - } -} - -// will load the given file and return it as dataURL. -// this way we can easily store whole files in the configuration/localStorage. -// its not safe that this works with something else than image files. -wewwApp.XHRLoadAndSaveLocal = (url, resCall) => { - // Create XHR and FileReader objects - var xhr = new XMLHttpRequest(); - var fileReader = new FileReader(); - xhr.open("GET", url, true); - xhr.responseType = "blob"; - xhr.addEventListener("load", function () { - if (xhr.status == 200) { - // onload needed since Google Chrome doesn't support addEventListener for FileReader - fileReader.onload = function (evt) { - // Read out file contents as a Data URL - resCall(evt.target.result) - }; - // Load blob as Data URL - fileReader.readAsDataURL(xhr.response); - } - }, false); - // Send XHR - xhr.send(); -} - -// apply object values/settings to html -wewwApp.UpdateSettings = () => { - var wewwaProps = wewwApp.project.general.properties; - localStorage.setItem("wewwaLastProps", JSON.stringify(wewwaProps)); - for (var p in wewwaProps) { - var prop = wewwaProps[p]; - - // some eval magic - var visible = true; - if (prop.condition != null) { - // copy our condition string to modify - var cprop = String(prop.condition).split(" ").join(""); - // remove whitespaces and split to partials by logic operators - var partials = cprop.split(/&&|\|\|/); - // loop all partial values of the check - for (var part of partials) { - var prefix = "wewwaProps."; - var onlyVal = part.match(/[!a-zA-Z0-9_\.]*/)[0]; - if (!onlyVal.startsWith(prefix) && !onlyVal.startsWith("!" + prefix)) { - // fix for inverted values - var replW = onlyVal; - if (replW.startsWith("!")) { - replW = replW.substr(1); - prefix = "!" + prefix; - } - //console.debug("replace: " + onlyVal + " >> " + prefix + replW); - cprop = cprop.replace(onlyVal, prefix + replW); - } - - } - try { - visible = eval(cprop) == true; - } - catch (e) { - console.error("Error: (" + cprop + ") for: " + p + " => " + e); - } - } - - - if (visible) $("#wewwa_" + p).fadeIn(); - else $("#wewwa_" + p).fadeOut(); - - // get input dom element - var elm = document.getElementById("wewwa_" + p).childNodes[1].childNodes[0]; - switch (prop.type) { - case "color": - elm.value = wewwApp.rgbToHex(prop.value); - break; - case "bool": - elm.checked = prop.value == true; - break; - case "slider": - case "combo": - case "textinput": - elm.value = prop.value; - break; - } - } -} - -// apply settings object to we -wewwApp.ApplyProp = (prop) => { - var wpl = window.wallpaperPropertyListener; - if (wpl && wpl.applyUserProperties) { - wpl.applyUserProperties(prop); - } -} - -// converts float "r g b" into to #XXXXXX hex string -wewwApp.rgbToHex = (rgb) => { - function cth(c) { - var h = Math.floor(c * 255).toString(16); - return h.length == 1 ? "0" + h : h; - } - var spl = rgb.split(" "); - return "#" + cth(spl[0]) + cth(spl[1]) + cth(spl[2]); -} - -// converts hex string to "r g b" float string -wewwApp.hexToRgb = (hex) => { - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255].join(" ") : null; -} - -// start microphone -wewwApp.requestMicrophone = () => { - navigator.mediaDevices.getUserMedia({ - audio: true - }).then(function (stream) { - wewwApp.stopAudioInterval(); - - window.persistAudioStream = stream; - - wewwApp.ctx = new (window.AudioContext || window.webkitAudioContext)(); - wewwApp.source = wewwApp.ctx.createMediaStreamSource(stream); - wewwApp.analyser = wewwApp.ctx.createAnalyser(); - wewwApp.analyser.smoothingTimeConstant = 0.35; - wewwApp.analyser.fftSize = 256; - - wewwApp.source.connect(wewwApp.analyser); - wewwApp.startAudioInterval(); - }).catch(function (err) { - console.error(err); - if (location.protocol != "https:") { - var r = confirm("Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS for this feature to work! Press 'ok'/'yes' to get redirected to HTTPS and try again."); - if (r) window.location.href = window.location.href.replace("http", "https"); - } - }); -} - -// html5 audio analyser gives us mono data from 0(bass) to 128(treble) -// however, wallpaper engine expects stereo data in following format: -// 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) -// so we do some array transformation... and divide by 255 (8bit-uint becomes float) -wewwApp.convertAudio = (data) => { - var stereo = []; - var sIdx = 0; - for (var i = 0; i < 64; i++) { - stereo[i] = data[sIdx++] / 255; - stereo[127 - i] = data[sIdx++] / 255; - } - return stereo; -} - -// starts playing & analysing a dropped file -wewwApp.initiateAudio = (data) => { - // clear up - wewwApp.stopAudioInterval(); - // create player - wewwApp.audio = document.createElement("audio"); - wewwApp.audio.src = data.name ? URL.createObjectURL(data) : data; - wewwApp.audio.autoplay = true; - wewwApp.audio.setAttribute("controls", "true"); - wewwApp.audio.play = true; - - $("#audioMarker").prepend(wewwApp.audio); - - wewwApp.ctx = new (window.AudioContext || window.webkitAudioContext)(); - wewwApp.source = wewwApp.ctx.createMediaElementSource(wewwApp.audio); - wewwApp.analyser = wewwApp.ctx.createAnalyser(); - wewwApp.analyser.smoothingTimeConstant = 0.35; - wewwApp.analyser.fftSize = 256; - - wewwApp.source.connect(wewwApp.ctx.destination); - wewwApp.source.connect(wewwApp.analyser); - wewwApp.startAudioInterval(); -} - -// starts audio analyser interval -wewwApp.startAudioInterval = () => { - var data = new Uint8Array(128); - // 33ms ~~ 30fps - wewwApp.audioInterval = setInterval(() => { - if (!wewwApp.audioCallback) { - wewwApp.stopAudioInterval(); - alert("no AudioCallback!"); - return; - } - wewwApp.analyser.getByteFrequencyData(data); - var stereo = wewwApp.convertAudio(data); - wewwApp.audioCallback(stereo); - }, 33); - // tell Wallpaper we are sending audio - wewwApp.ApplyProp({ audioprocessing: { value: true } }) -} - -// stops audio analyser interval and playing audio -wewwApp.stopAudioInterval = () => { - window.persistAudioStream = null; - $("#wewwaAudioInput").val(""); - if (wewwApp.audio) - wewwApp.audio.remove(); - if (wewwApp.audioInterval) { - clearInterval(wewwApp.audioInterval); - wewwApp.audioInterval = null; - } -} - -if (window.wallpaperRegisterAudioListener) console.info("[WEWWA] detected wallpaper engine => Standby."); -else { - console.info("[WEWWA] wallpaper engine not detected => Init!"); - // define audio listener first, so we dont miss when it gets registered. - window.wallpaperRegisterAudioListener = function (callback) { - // set callback to be called later with analysed audio data - wewwApp.audioCallback = callback; - } - // ready helper - const ready = function (fn) { - if (typeof fn !== 'function') - return false; - // If document is already loaded, run method - if (document.readyState === 'interactive' || document.readyState === 'complete') - return fn(); - // Otherwise, wait until document is loaded - document.addEventListener('DOMContentLoaded', fn, false); - } - // intialize when ready - ready(() => wewwApp.Init()); -} \ No newline at end of file From d9664c545483ac488e17da65b56fc86c8ea38dff Mon Sep 17 00:00:00 2001 From: hexxone Date: Sat, 28 Nov 2020 20:55:25 +0100 Subject: [PATCH 09/76] Fixing Audio & Offline workers --- src/OfflineHelper.ts | 11 ++-- src/OfflineWorker.ts | 30 ++++++----- src/WEAS.ts | 9 ++-- src/WEASWorker.ts | 4 +- src/WEWWA.ts | 121 ++++++++++++++++++++++++------------------- 5 files changed, 96 insertions(+), 79 deletions(-) diff --git a/src/OfflineHelper.ts b/src/OfflineHelper.ts index ee2c891..694bf5b 100644 --- a/src/OfflineHelper.ts +++ b/src/OfflineHelper.ts @@ -22,14 +22,15 @@ import { Smallog } from "./Smallog"; import OfflineWorker from 'worker-loader!./OfflineWorker'; export module OfflineHelper { - // function helper, so OfflineWorker is actually packed + // function helper, so OfflineWorker is actually processed function DontRemove() { return new OfflineWorker() }; - export async function Register(workerPath: string = "OfflineWorker.worker.js?jsonPath=offlinefiles.json") { + export async function Register(call: any = null, workerPath: string = "OfflineWorker.worker.js?jsonPath=offlinefiles.json") { if ('serviceWorker' in navigator) { - await navigator.serviceWorker.register(workerPath).then( - () => Smallog.Info('[OfflineHelper]: service-worker registration complete.'), - () => Smallog.Error('[OfflineHelper]: service-worker registration failure.')); + await navigator.serviceWorker.register(workerPath) + .then(() => Smallog.Info('[OfflineHelper]: service-worker registration complete.'), + () => Smallog.Error('[OfflineHelper]: service-worker registration failure.')) + .then(() => call ? call() : null); return true; } else Smallog.Info('[OfflineHelper]: service-worker is not supported.'); diff --git a/src/OfflineWorker.ts b/src/OfflineWorker.ts index 67bcc1a..3c0a97d 100644 --- a/src/OfflineWorker.ts +++ b/src/OfflineWorker.ts @@ -19,18 +19,20 @@ const wrk: ServiceWorker = self as any; -console.info('[OfflineWorker] : executing.'); + // A version number is useful when updating the worker logic, // allowing you to remove outdated cache entries during the update. -var version = 'v1::'; +const version = 'v1::'; +const wName = "[OfflineWorker] "; + +console.info(wName + 'executing.'); // The install event fires when the service worker is first installed. // You can use this event to prepare the service worker to be able to serve // files while visitors are offline. - wrk.addEventListener("install", function (event: any) { - console.info('[OfflineWorker] : install event in progress.'); + console.info(wName + 'install event in progress.'); // get the path for the .json file which contains the files to-be-cached // These resources will be downloaded and cached by the service worker @@ -48,7 +50,7 @@ wrk.addEventListener("install", function (event: any) { // then map the array and add one-by-one .then(cache => data.map((url) => cache.add(url)))) // log success - .then(() => console.info('[OfflineWorker] : install completed')) + .then(() => console.info(wName + 'install completed')) ); }); @@ -57,9 +59,9 @@ wrk.addEventListener("install", function (event: any) { // comprehends even the request for the HTML page on first load, as well as JS and // CSS resources, fonts, any images, etc. wrk.addEventListener("fetch", function (event: any) { - console.info('[OfflineWorker] : fetch event in progress.'); + console.info(wName + 'fetch event in progress.'); if (event.request.method !== 'GET') { - console.info('[OfflineWorker] : fetch event ignored.', event.request.method, event.request.url); + console.info(wName + 'fetch event ignored.', event.request.method, event.request.url); return; } @@ -71,7 +73,7 @@ wrk.addEventListener("fetch", function (event: any) { .then(async (cached) => { // return immediately if cache successfull if (cached) { - console.info('[OfflineWorker] : fetch event(cached): ', event.request.url); + console.info(wName + 'fetch event(cached): ', event.request.url); return cached } @@ -83,11 +85,11 @@ wrk.addEventListener("fetch", function (event: any) { // This is the response that will be stored on the ServiceWorker cache. function fetchedFromNetwork(response) { var cacheCopy = response.clone(); - console.info('[OfflineWorker] : fetch response from network.', event.request.url); + console.info(wName + 'fetch response from network.', event.request.url); // We open a cache to store the response for this request. caches.open(version + 'pages') .then((cache) => cache.put(event.request, cacheCopy)) - .then(() => console.info('[OfflineWorker] : fetch response stored in cache.', event.request.url)); + .then(() => console.info(wName + 'fetch response stored in cache.', event.request.url)); // Return the response so that the promise is settled in fulfillment. return response; } @@ -104,7 +106,7 @@ wrk.addEventListener("fetch", function (event: any) { - Generate a Response programmaticaly, as shown below, and return that. */ function unableToResolve() { - console.info('[OfflineWorker] : fetch request failed in both cache and network.'); + console.info(wName + 'fetch request failed in both cache and network.'); return new Response('Service Unavailable', { status: 503, statusText: 'Service Unavailable', @@ -119,7 +121,7 @@ wrk.addEventListener("fetch", function (event: any) { // We handle the network request with success and failure scenarios. .then(fetchedFromNetwork, unableToResolve) // we are done - .then(() => console.info('[OfflineWorker] : fetch event(networked): ', event.request.url)) + .then(() => console.info(wName + 'fetch event(networked): ', event.request.url)) // We should catch errors on the fetchedFromNetwork handler as well. .catch(unableToResolve); }) @@ -133,7 +135,7 @@ wrk.addEventListener("fetch", function (event: any) { installing. */ wrk.addEventListener("activate", function (event: any) { - console.info('[OfflineWorker] : activate event in progress.'); + console.info(wName + 'activate event in progress.'); event.waitUntil(async () => { // Feature-detect navigation preloads! if (self['registration'] && self['registration'].navigationPreload) { @@ -149,7 +151,7 @@ wrk.addEventListener("activate", function (event: any) { // Return a promise that's fulfilled when each outdated cache is deleted. .map((key) => caches.delete(key)))) // completed - .then(() => console.info('[OfflineWorker] : activate completed.')); + .then(() => console.info(wName + 'activate completed.')); }); // Tell the active service worker to take control of the page immediately. diff --git a/src/WEAS.ts b/src/WEAS.ts index 228a3de..151b9b6 100644 --- a/src/WEAS.ts +++ b/src/WEAS.ts @@ -74,7 +74,7 @@ export class WEAS extends CComponent { private realInit() { // if wallpaper engine context given, listen - if (!['wallpaperRegisterAudioListener']) { + if (!window['wallpaperRegisterAudioListener']) { Smallog.Info("'window.wallpaperRegisterAudioListener' not given!"); return; } @@ -87,7 +87,7 @@ export class WEAS extends CComponent { this.weasWorker.onmessage = (e) => { e.data.data = new Float32Array(e.data.data); self.lastAudio = e.data; - Smallog.Debug("Got Data from Worker: " + JSON.stringify(e.data)); + Smallog.Debug("Got Data from Worker! Offset= " + (performance.now() / 1000 - e.data.time) + ", Data= " + JSON.stringify(e.data)); }; // worker Error @@ -108,13 +108,14 @@ export class WEAS extends CComponent { // post web worker task this.weasWorker.postMessage({ settings: this.GetSettingsObj(), - last: this.lastAudio, + last: Object.assign({}, this.lastAudio), + time: performance.now() / 1000, audio: audBuff.buffer }, [audBuff.buffer]); }); } - hasAudio() { + public hasAudio() { // return false if there is no data or its invalid due to time (> 3s old) return this.lastAudio && !this.lastAudio.silent && (performance.now() / 1000 - this.lastAudio.time < 3); diff --git a/src/WEASWorker.ts b/src/WEASWorker.ts index c598738..791e767 100644 --- a/src/WEASWorker.ts +++ b/src/WEASWorker.ts @@ -54,7 +54,7 @@ class AudioProcessor { if (settings.value_smoothing > 0) this.smoothArray(data, settings.value_smoothing); // process with last data? - if (lastData) this.applyValueLeveling(data, lastData.data, settings); + if (lastData && lastData.data) this.applyValueLeveling(data, lastData.data, settings); } // correct pink noise for first and second half @@ -193,7 +193,7 @@ ctx.addEventListener("message", (e) => { range: max - min, silent: (max < settings.minimum_volume / 1000), intensity: (bass * 8 - mids + peaks) / 6 / average, - time: performance.now() / 1000, + time: eventData.time, data: data.buffer, }, [data.buffer]); }); diff --git a/src/WEWWA.ts b/src/WEWWA.ts index 12f62bf..b100b0b 100644 --- a/src/WEWWA.ts +++ b/src/WEWWA.ts @@ -9,8 +9,8 @@ * @see * REQUIREMENTS: * - JQUERY >= 3.3.1 - * - HTML5 supported Browser (for webAudio processing) - * - this file needs to be in the same (root) folder as your "project.json" + * - HTML5 Browser + * - the "project.json" needs to be in the root folder like "index.html" * - this file needs to be included/loaded in your "index.html" * * @description @@ -24,6 +24,7 @@ * - automatically detecting if the web wallpaper is opened by wallpaper engine or browser * - if opened by wallpaper engine, nothing will happen * - if opened by a browser: + * - use a ServiceWorker to make page always available offline * - automatically load the "project.json" * - parse the settings, languages & conditions * - add respective html elements for each setting type & condition @@ -33,6 +34,8 @@ * - react to changes made in the ui and update them in the wallpaper * - save changes made in the ui to localStorage * + * @todo + * - inject "audio processing" setting */ import { Ready } from "./Ready"; @@ -55,26 +58,33 @@ export class WEWWA { private audioCallback: any = null; - constructor() { - if (window['wallpaperRegisterAudioListener']) Smallog.Info("[WEWWA] detected wallpaper engine => Standby."); - else { - Smallog.Info("[WEWWA] wallpaper engine not detected => Init!"); - // define audio listener first, so we dont miss when it gets registered. - window['wallpaperRegisterAudioListener'] = function (callback) { - // set callback to be called later with analysed audio data - this.audioCallback = callback; - } - // intialize when ready - Ready.On(() => { - // make the website available offline using service worker - OfflineHelper.Register(); + constructor(finished) { + if (window['wallpaperRegisterAudioListener']) { + Smallog.Info("[WEWWA] detected wallpaper engine => Standby."); + finished(); + return; + } + + Smallog.Info("[WEWWA] wallpaper engine not detected => Init!"); + + // define audio listener first, so we dont miss when it gets registered. + window['wallpaperRegisterAudioListener'] = (callback) => { + // set callback to be called later with analysed audio data + this.audioCallback = callback; + } + + // intialize when ready + Ready.On(() => { + // make the website available offline using service worker + OfflineHelper.Register(() => { // continue initializing + finished(); this.Init(); }); - } + }); } - Init = () => { + private Init() { this.LoadProjectJSON((proj) => { if (proj.type != "web") { Smallog.Error("Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting..."); @@ -89,7 +99,7 @@ export class WEWWA { }); } - LoadProjectJSON = (complete) => { + private LoadProjectJSON(complete) { $.ajax({ url: "project.json", beforeSend: (xhr) => xhr.overrideMimeType("text/plain;"), @@ -98,7 +108,7 @@ export class WEWWA { }); } - LoadStorage = () => { + private LoadStorage() { var props = this.project.general.properties; var last = localStorage.getItem("wewwaLastProps"); if (last != null) { @@ -108,7 +118,7 @@ export class WEWWA { } } - AddStyle = () => { + private AddStyle() { var st = document.createElement("style"); st.innerHTML = ` .wewwaMenu, .wewwaIcon { @@ -202,7 +212,9 @@ export class WEWWA { document.head.append(st); } - AddMenu = (lang) => { + private AddMenu(lang) { + var self = this; + if (this.htmlMenu) { document.body.removeChild(this.htmlMenu); document.body.removeChild(this.htmlIcon); @@ -247,21 +259,21 @@ export class WEWWA { var aBtn2 = ce("a"); var aBtn3 = ce("input"); aBtn1.innerHTML = "Microphone"; - aBtn1.addEventListener("click", function (e) { - this.requestMicrophone(); + aBtn1.addEventListener("click", e => { + self.requestMicrophone(); }); aBtn2.innerHTML = "Select URL"; - aBtn2.addEventListener("click", function (e) { + aBtn2.addEventListener("click", e => { var uri = prompt("Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!", "https://example.com/test.mp3"); - this.initiateAudio(uri); + self.initiateAudio(uri); }); aBtn3.id = "wewwaAudioInput"; aBtn3.innerHTML = "Select File"; aBtn3.setAttribute("type", "file"); - aBtn3.addEventListener("change", function (e) { + aBtn3.addEventListener("change", e => { var file = e.target.files[0]; if (!file) return; - this.initiateAudio(file); + self.initiateAudio(file); }); td1.append(aBtn1, aBtn2, aBtn3); row.append(td1); @@ -279,11 +291,11 @@ export class WEWWA { evt.preventDefault(); evt.dataTransfer.dropEffect = 'copy'; }, false); - dropArea.addEventListener("drop", (e) => { + dropArea.addEventListener("drop", e => { e.stopPropagation(); e.preventDefault(); var droppedFiles = e.dataTransfer.files; - this.initiateAudio(droppedFiles[0]); + self.initiateAudio(droppedFiles[0]); }, false); dropt1.append(dropArea); dropRow.append(dropt1, dropt2); @@ -294,8 +306,8 @@ export class WEWWA { var hrtd2 = ce("td"); var hrstop = ce("a"); hrstop.innerHTML = "Stop All Audio"; - hrstop.addEventListener("click", function (e) { - this.stopAudioInterval(); + hrstop.addEventListener("click", e => { + self.stopAudioInterval(); }); var hrhr = ce("hr") hrtd1.id = "audioMarker"; @@ -351,9 +363,9 @@ export class WEWWA { // if changed, do it all over again. lan.addEventListener("change", function (e) { localStorage.setItem("wewwaLang", this.value); - this.AddMenu(this.value); - this.UpdateSettings(); - this.html.icon.click(); + self.AddMenu(this.value); + self.UpdateSettings(); + (self.htmlIcon as any).click(); }); td2.setAttribute("colspan", 2); td2.append(lan); @@ -379,7 +391,7 @@ export class WEWWA { preFoot.innerHTML = "

"; var reset = ce("a"); reset.innerHTML = "Reset Settings"; - reset.addEventListener("click", function (e) { + reset.addEventListener("click", e => { localStorage.clear(); location = location; }); @@ -408,7 +420,9 @@ export class WEWWA { this.htmlIcon = icon; } - CreateItem = (prop, itm) => { + private CreateItem(prop, itm) { + var self = this; + var ce = (e) => document.createElement(e); var row = ce("tr"); row.setAttribute("id", "wewwa_" + prop); @@ -462,7 +476,7 @@ export class WEWWA { sliderVal.style.width = "75%"; if (canEdit) { sliderVal.setAttribute("value", itm.value); - sliderVal.addEventListener("change", function (e) { this.SetProperty(prop, this); }); + sliderVal.addEventListener("change", function (e) { self.SetProperty(prop, this); }); } else { sliderVal.innerHTML = itm.value; @@ -490,7 +504,7 @@ export class WEWWA { td1.append(txt); // listen for changes if input type (no text) if (inp) { - inp.addEventListener("change", function (e) { this.SetProperty(prop, this); }); + inp.addEventListener("change", function (e) { self.SetProperty(prop, this); }); td2.prepend(inp); } // append td3 or stretch td2? @@ -505,7 +519,7 @@ export class WEWWA { } - SetProperty = (prop, elm) => { + public SetProperty(prop, elm) { // get the type and apply the value var props = this.project.general.properties; // check for legit setting... @@ -555,11 +569,12 @@ export class WEWWA { } } + // TODO REMVOE ? // will load the given file and return it as dataURL. // this way we can easily store whole files in the configuration/localStorage. // its not safe that this works with something else than image files. - XHRLoadAndSaveLocal = (url, resCall) => { + private XHRLoadAndSaveLocal(url, resCall) { // Create XHR and FileReader objects var xhr = new XMLHttpRequest(); var fileReader = new FileReader(); @@ -581,7 +596,7 @@ export class WEWWA { } - UpdateSettings = () => { + private UpdateSettings() { var wewwaProps = this.project.general.properties; localStorage.setItem("wewwaLastProps", JSON.stringify(wewwaProps)); for (var p in wewwaProps) { @@ -608,7 +623,6 @@ export class WEWWA { //Smallog.Debug("replace: " + onlyVal + " >> " + prefix + replW); cprop = cprop.replace(onlyVal, prefix + replW); } - } try { visible = eval(cprop) == true; @@ -640,15 +654,14 @@ export class WEWWA { } } - - ApplyProp = (prop) => { + public ApplyProp(prop) { var wpl = window['wallpaperPropertyListener']; if (wpl && wpl.applyUserProperties) { wpl.applyUserProperties(prop); } } - rgbToHex = (rgb) => { + private rgbToHex(rgb) { function cth(c) { var h = Math.floor(c * 255).toString(16); return h.length == 1 ? "0" + h : h; @@ -657,15 +670,15 @@ export class WEWWA { return "#" + cth(spl[0]) + cth(spl[1]) + cth(spl[2]); } - hexToRgb = (hex) => { + private hexToRgb(hex) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255].join(" ") : null; } - requestMicrophone = () => { + private requestMicrophone() { navigator.mediaDevices.getUserMedia({ audio: true - }).then(function (stream) { + }).then((stream) => { this.stopAudioInterval(); // hack for firefox to keep stream running window['persistAudioStream'] = stream; @@ -677,7 +690,7 @@ export class WEWWA { this.source.connect(this.analyser); this.startAudioInterval(); - }).catch(function (err) { + }).catch((err) => { Smallog.Error(err); if (location.protocol != "https:") { var r = confirm("Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS for this feature to work! Press 'ok'/'yes' to get redirected to HTTPS and try again."); @@ -690,7 +703,7 @@ export class WEWWA { // however, wallpaper engine expects stereo data in following format: // 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) // so we do some array transformation... and divide by 255 (8bit-uint becomes float) - convertAudio = (data) => { + private convertAudio(data) { var stereo = []; var sIdx = 0; for (var i = 0; i < 64; i++) { @@ -701,7 +714,7 @@ export class WEWWA { } - initiateAudio = (data) => { + private initiateAudio(data) { // clear up this.stopAudioInterval(); // create player @@ -725,11 +738,11 @@ export class WEWWA { } - startAudioInterval = () => { + private startAudioInterval() { var data = new Uint8Array(128); // 33ms ~~ 30fps this.audioInterval = setInterval(() => { - if (this.audioCallback != null) { + if (this.audioCallback == null) { this.stopAudioInterval(); alert("no AudioCallback!"); return; @@ -742,7 +755,7 @@ export class WEWWA { this.ApplyProp({ audioprocessing: { value: true } }) } - stopAudioInterval = () => { + public stopAudioInterval() { window['persistAudioStream'] = null; $("#wewwaAudioInput").val(""); if (this.audio) From 677d37ba44fc26ff4eccaf6f62c6f5bc41a29d91 Mon Sep 17 00:00:00 2001 From: hexxone Date: Mon, 30 Nov 2020 23:04:43 +0100 Subject: [PATCH 10/76] Add Cookie Stuff --- src/WEAS.ts | 4 +--- src/WEWWA.ts | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/WEAS.ts b/src/WEAS.ts index 151b9b6..22d36b4 100644 --- a/src/WEAS.ts +++ b/src/WEAS.ts @@ -67,9 +67,7 @@ export class WEAS extends CComponent { constructor() { super(); // delay audio initialization for some time - Ready.On(() => { - setTimeout(() => this.realInit(), 3000); - }); + Ready.On(() => this.realInit()); } private realInit() { diff --git a/src/WEWWA.ts b/src/WEWWA.ts index b100b0b..e185d19 100644 --- a/src/WEWWA.ts +++ b/src/WEWWA.ts @@ -36,11 +36,14 @@ * * @todo * - inject "audio processing" setting + * - Annoying Cookie Popup (Thanks DSGVO) + * */ import { Ready } from "./Ready"; import { Smallog } from "./Smallog"; import { OfflineHelper } from "./OfflineHelper"; +import { CC } from "cookieconsent"; export class WEWWA { @@ -75,6 +78,20 @@ export class WEWWA { // intialize when ready Ready.On(() => { + if(CC) {} + const cc = window['cookieconsent'].initialise({ + palette: { + popup: { + background: "#000" + }, + button: { + background: "#f1d600" + } + }, + position: "bottom-left", + theme: "edgeless" + }); + // make the website available offline using service worker OfflineHelper.Register(() => { // continue initializing @@ -572,7 +589,7 @@ export class WEWWA { // TODO REMVOE ? // will load the given file and return it as dataURL. - // this way we can easily store whole files in the configuration/localStorage. + // this way we can easily store whole files in the configuration & localStorage. // its not safe that this works with something else than image files. private XHRLoadAndSaveLocal(url, resCall) { // Create XHR and FileReader objects From c5c1f75a9e965b220bef3990ee4bbcda05a27773 Mon Sep 17 00:00:00 2001 From: hexxone Date: Thu, 10 Dec 2020 15:56:49 +0100 Subject: [PATCH 11/76] small Log & Worker improvements --- src/CComponent.ts | 2 +- src/CSettings.ts | 6 ++++-- src/OfflineHelper.ts | 43 ++++++++++++++++++++++++++++++--------- src/OfflinePlugin.js | 27 +++++++++++++++++++++++-- src/OfflineWorker.ts | 5 ++--- src/ReloadHelper.ts | 8 ++++---- src/Smallog.ts | 32 +++++++++++++++++++++-------- src/WEICUE.ts | 48 +++++++++++++++++++++----------------------- src/WEWWA.ts | 37 +++++++++++++++++++--------------- src/WarnHelper.ts | 6 +++--- 10 files changed, 140 insertions(+), 74 deletions(-) diff --git a/src/CComponent.ts b/src/CComponent.ts index b488565..99e1ea7 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -2,7 +2,7 @@ * @author D.Thiele @https://hexx.one * * @description - * represents a modular component for Wallpaper engine + * represents a Core Component for Wallpaper Engine Wallpaper * */ diff --git a/src/CSettings.ts b/src/CSettings.ts index 1904d36..2a05f10 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -7,8 +7,10 @@ * See LICENSE file in the project root for full license information. * * @description - * Settings interface, used for type-secure setting applying. - * All Settings-classes should be dereived frrom this one. + * Core Settings interface, used for type-secure setting applying. + * + * All Settings-classes should be dereived from this one. + * */ import { Smallog } from "./Smallog"; diff --git a/src/OfflineHelper.ts b/src/OfflineHelper.ts index 694bf5b..ee8630a 100644 --- a/src/OfflineHelper.ts +++ b/src/OfflineHelper.ts @@ -21,19 +21,42 @@ import { Smallog } from "./Smallog"; // this should pack the serviceworker like a webwoker. import OfflineWorker from 'worker-loader!./OfflineWorker'; +const LogHead = "[OfflineHelper] "; +const LogErrS = "service-worker is not supported."; + export module OfflineHelper { // function helper, so OfflineWorker is actually processed function DontRemove() { return new OfflineWorker() }; - export async function Register(call: any = null, workerPath: string = "OfflineWorker.worker.js?jsonPath=offlinefiles.json") { - if ('serviceWorker' in navigator) { - await navigator.serviceWorker.register(workerPath) - .then(() => Smallog.Info('[OfflineHelper]: service-worker registration complete.'), - () => Smallog.Error('[OfflineHelper]: service-worker registration failure.')) - .then(() => call ? call() : null); - return true; - } - else Smallog.Info('[OfflineHelper]: service-worker is not supported.'); - return false; + export function Register(workerPath: string = "js/OfflineWorker.worker.js?jsonPath=/js/offlinefiles.json") { + return new Promise(async (resolve) => { + if ('serviceWorker' in navigator) { + await navigator.serviceWorker.register(workerPath) + .then(() => Smallog.Info('service-worker registration complete.', LogHead), + () => Smallog.Error('service-worker registration failure.', LogHead)) + .then(() => resolve(true)); + return true; + } + else { + Smallog.Error(LogErrS, LogHead); + resolve(false); + } + }); + } + + export async function Reset() { + return new Promise((resolve) => { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.getRegistrations().then(async registrations => { + for (let registration of registrations) + await registration.unregister(); + resolve(true); + }); + } + else { + Smallog.Error(LogErrS, LogHead); + resolve(false); + } + }); } } \ No newline at end of file diff --git a/src/OfflinePlugin.js b/src/OfflinePlugin.js index 5cb9419..19efc8b 100644 --- a/src/OfflinePlugin.js +++ b/src/OfflinePlugin.js @@ -1,9 +1,32 @@ +/** + * + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * This is a webpack plugin for easily making your webapp available Offline. + * It will simply output a json list of files to cache for the Service Worker on build. + * + * In your source, you just 'require' and 'Register()' the OfflineHelper. + * The OfflineHelper will then require the OfflineWorker in background. + * + * The OfflineWorker will then: + * 1. launch itself + * 2. load the 'offlinefiles.json' list + * 3. cache all files in it + * 4. return cached files if network fetching fails + * + * @see + * You can also ignore this plugin and create the 'offlinefiles.json' yourself. + */ const fs = require("fs"); const validate = require('schema-utils'); - -// eslint-disable-next-line global-require const { RawSource } = require('webpack-sources'); const pluginName = 'OfflinePlugin'; diff --git a/src/OfflineWorker.ts b/src/OfflineWorker.ts index 3c0a97d..1b01b80 100644 --- a/src/OfflineWorker.ts +++ b/src/OfflineWorker.ts @@ -10,6 +10,7 @@ * @description * Generic Service Worker for Caching an app and making it available offline. * Needs to be passed the `?jsonPath=` argument with a path to a file json. + * Everything else is explained in between... * * @see * Read more: https://ponyfoo.com/articles/progressive-networking-serviceworker @@ -19,11 +20,9 @@ const wrk: ServiceWorker = self as any; - - // A version number is useful when updating the worker logic, // allowing you to remove outdated cache entries during the update. -const version = 'v1::'; +const version = 'v2.4::'; const wName = "[OfflineWorker] "; console.info(wName + 'executing.'); diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index 102e475..bdeb672 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -15,7 +15,7 @@ export class ReloadHelper { }); } - injectCSS() { + private injectCSS() { var st = document.createElement("style"); st.innerHTML = ` #reload-bar { @@ -60,7 +60,7 @@ export class ReloadHelper { document.head.append(st); } - injectHTML() { + private injectHTML() { var outer = document.createElement("div"); outer.id = "reloader"; var bar = document.createElement("div"); @@ -72,11 +72,11 @@ export class ReloadHelper { document.body.append(outer); } - Show() { + public Show() { $("#reload-bar, #reload-text").removeClass("done").addClass("show"); } - Hide() { + public Hide() { $("#reload-bar, #reload-text").removeClass("show").addClass("done"); } }; \ No newline at end of file diff --git a/src/Smallog.ts b/src/Smallog.ts index d5c5975..739358c 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -2,6 +2,21 @@ * @author D.Thiele @https://hexx.one */ + +function TraceCall(def: string) { + try { + throw new Error("TraceCall()"); + } + catch (e) { + // Examine e.stack here + if (e.stack) { + const splt = e.stack.split(/\n/); + if (splt.length > 3) return "[" + splt[3].trim().substring(3) + "] "; + } + } + return def; +} + export enum LogLevel { Error = 0, Info = 1, @@ -26,21 +41,22 @@ export module Smallog { preFix = pre; } - export function Error(msg: string) { - Log(console.error, msg); + export function Error(msg: string, hdr: string = preFix) { + Log(console.error, msg, TraceCall(hdr)); } - export function Info(msg: string) { - if (logLevel >= 1) Log(console.info, msg); + export function Info(msg: string, hdr: string = preFix) { + if (logLevel >= 2) hdr = TraceCall(hdr); + if (logLevel >= 1) Log(console.info, msg, hdr); } - export function Debug(msg: string) { - if (logLevel >= 2) Log(console.debug, msg); + export function Debug(msg: string, hdr: string = preFix) { + if (logLevel >= 2) Log(console.debug, msg, TraceCall(hdr)); } - function Log(call: any, msg: string) { + function Log(call: any, msg: string, hdr: string) { var m = msg; if (printTime) m = ("[" + new Date().toLocaleString() + "] ") + m; - call(preFix + m); + call(hdr + m); } } \ No newline at end of file diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 7655859..63d86eb 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -24,6 +24,10 @@ import { Ready } from "./Ready"; import { Smallog } from "./Smallog"; import { WEAS } from "./WEAS"; +const ClassName: string = "[WEICUE] "; +const canvasX: number = 23; +const canvasY: number = 7; + export class CUESettings extends CSettings { icue_mode: number = 1; icue_area_xoff: number = 50; @@ -41,21 +45,17 @@ export class CUESettings extends CSettings { export class WEICUE extends CComponent { private weas: WEAS = null; - - public settings: CUESettings = new CUESettings(); + private preview: HTMLDivElement = null; + private helperCanvas: HTMLCanvasElement = null; + private helperContext: CanvasRenderingContext2D = null; + private icueDevices = []; + private icueInterval = null; // runtime values - PAUSED = false; - isAvailable = false; - canvasX = 23; - canvasY = 7; - icueDevices = []; - preview = null; - icueInterval = null; - helperCanvas = null; - helperContext = null; - - + public settings: CUESettings = new CUESettings(); + public isAvailable: boolean = false; + public PAUSED: boolean = false; + constructor(weas: WEAS) { super(); this.weas = weas; @@ -63,7 +63,7 @@ export class WEICUE extends CComponent { // Plugin handler window['wallpaperPluginListener'] = { onPluginLoaded: function (name, version) { - Smallog.Info("Plugin: " + name + ", Version: " + version + " : registered."); + Smallog.Info("Plugin: " + name + ", Version: " + version + " : registered.", ClassName); if (name === 'cue') this.isAvailable = true; if (name === 'led') this.isAvailable = true; } @@ -289,7 +289,7 @@ AAAASUVORK5CYII= // show a message by icue icueMessage(msg) { - Smallog.Debug("WEICUE MSG: " + msg); + Smallog.Debug("MSG: " + msg, ClassName); $("#icueholder").css('opacity', 1); $("#icuetext").html(msg); $("#icueholder").fadeIn({ queue: false, duration: "slow" }); @@ -383,7 +383,7 @@ AAAASUVORK5CYII= this.initCUE(0); - Smallog.Debug("weiCUE: init..."); + Smallog.Debug("init...", ClassName); // recreate if reinit if (this.icueInterval) clearInterval(this.icueInterval); @@ -391,8 +391,8 @@ AAAASUVORK5CYII= // setup canvas this.helperCanvas = document.createElement("canvas"); this.helperCanvas.id = "helpCvs"; - this.helperCanvas.width = this.canvasX; - this.helperCanvas.height = this.canvasY; + this.helperCanvas.width = canvasX; + this.helperCanvas.height = canvasY; this.helperCanvas.style.display = "none"; this.helperContext = this.helperCanvas.getContext("2d"); document.body.appendChild(this.helperCanvas); @@ -437,10 +437,10 @@ AAAASUVORK5CYII= // projection mode if (sett.icue_mode == 1) { // get scaled down image data and encode it for icue - var encDat = this.getEncodedCanvasImageData(this.helperContext.getImageData(0, 0, this.canvasX, this.canvasY)); + var encDat = this.getEncodedCanvasImageData(this.helperContext.getImageData(0, 0, canvasX, canvasY)); // update all icueDevices with data for (var xi = 0; xi < this.icueDevices.length; xi++) { - window['cue'].setLedColorsByImageData(xi, encDat, this.canvasX, this.canvasY); + window['cue'].setLedColorsByImageData(xi, encDat, canvasX, canvasY); } } // color mode @@ -476,18 +476,16 @@ AAAASUVORK5CYII= if (sett.icue_mode == 1) { // get helper vars - var cueWid = this.canvasX; - var cueHei = this.canvasY; - var area = this.getArea(); + var area: any = this.getArea(false); var hctx = this.helperContext; // get real rgb values var spl = sett.main_color.split(' ') as unknown[]; for (var i = 0; i < spl.length; i++) spl[i] = (spl[i] as number * 255); // overlay "decay" style hctx.fillStyle = "rgba(" + spl.join(", ") + ", " + sett.icue_area_decay / 100 + ")"; - hctx.fillRect(0, 0, cueWid, cueHei); + hctx.fillRect(0, 0, canvasX, canvasY); // scale down and copy the image to the helper canvas - hctx.drawImage(mainCanvas, area.left, area.top, area.width, area.height, 0, 0, cueWid, cueHei); + hctx.drawImage(mainCanvas, area.left, area.top, area.width, area.height, 0, 0, canvasX, canvasY); // blur the helper projection canvas if (sett.icue_area_blur > 0) this.gBlurCanvas(this.helperCanvas, hctx, sett.icue_area_blur); } diff --git a/src/WEWWA.ts b/src/WEWWA.ts index e185d19..a4fe695 100644 --- a/src/WEWWA.ts +++ b/src/WEWWA.ts @@ -45,6 +45,8 @@ import { Smallog } from "./Smallog"; import { OfflineHelper } from "./OfflineHelper"; import { CC } from "cookieconsent"; +const LogHead = "[WEWWA] "; + export class WEWWA { private project: any = null; @@ -63,12 +65,12 @@ export class WEWWA { constructor(finished) { if (window['wallpaperRegisterAudioListener']) { - Smallog.Info("[WEWWA] detected wallpaper engine => Standby."); + Smallog.Info("detected wallpaper engine => Standby.", LogHead); finished(); return; } - Smallog.Info("[WEWWA] wallpaper engine not detected => Init!"); + Smallog.Info("wallpaper engine not detected => Init!", LogHead); // define audio listener first, so we dont miss when it gets registered. window['wallpaperRegisterAudioListener'] = (callback) => { @@ -93,7 +95,7 @@ export class WEWWA { }); // make the website available offline using service worker - OfflineHelper.Register(() => { + OfflineHelper.Register().then(() => { // continue initializing finished(); this.Init(); @@ -104,7 +106,7 @@ export class WEWWA { private Init() { this.LoadProjectJSON((proj) => { if (proj.type != "web") { - Smallog.Error("Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting..."); + Smallog.Error("Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...", LogHead); return; } this.project = proj; @@ -121,7 +123,7 @@ export class WEWWA { url: "project.json", beforeSend: (xhr) => xhr.overrideMimeType("text/plain;"), success: (result) => complete(JSON.parse(result)), - error: (xhr, status, error) => Smallog.Error(status + ": ajax error!\r\n" + error) + error: (xhr, status, error) => Smallog.Error(status + ": ajax error!\r\n" + error, LogHead) }); } @@ -131,7 +133,7 @@ export class WEWWA { if (last != null) { var merged = Object.assign(props, JSON.parse(last)); this.project.general.properties = merged; - Smallog.Debug("Loaded & merged settings.") + Smallog.Debug("Loaded & merged settings.", LogHead) } } @@ -409,14 +411,17 @@ export class WEWWA { var reset = ce("a"); reset.innerHTML = "Reset Settings"; reset.addEventListener("click", e => { - localStorage.clear(); - location = location; + if(!window.confirm("This action will clear ALL local data!\r\n\r\nAre you sure?")) return; + OfflineHelper.Reset().then(() => { + localStorage.clear(); + location = location; + }); }); preFoot.append(reset); // footer with ident var footer = ce("div"); - footer.innerHTML = "

[W] allpaper
[E] ngine
[W] eb
[W] allpaper
[A] dapterhexxone"; + footer.innerHTML = "

[W] allpaper
[E] ngine
[W] eb
[W] allpaper
[A] dapterby hexxone"; // finish up menu menu.append(preview, header, link, tmain, preFoot, footer) @@ -515,7 +520,7 @@ export class WEWWA { inp.setAttribute("type", "file"); break; default: - Smallog.Error("unkown setting type: " + itm.type); + Smallog.Error("unkown setting type: " + itm.type, LogHead); break; } td1.append(txt); @@ -541,7 +546,7 @@ export class WEWWA { var props = this.project.general.properties; // check for legit setting... if (!props[prop]) { - Smallog.Error("SetProperty name not found!"); + Smallog.Error("SetProperty name not found: " + prop, LogHead); return; } // enabled delayed call of settings update @@ -572,12 +577,12 @@ export class WEWWA { if (elm.name.includes("_out_")) { var inpt: any = document.querySelector("#wewwa_" + prop); if (inpt) inpt.value = elm.value; - else Smallog.Error("Slider not found: " + prop); + else Smallog.Error("Slider not found: " + prop, LogHead); } else { var slide: any = document.querySelector("#wewwa_out_" + prop); if (slide) slide.value = elm.value; - else Smallog.Error("Numericupdown not found: " + prop); + else Smallog.Error("Numericupdown not found: " + prop, LogHead); } case "combo": case "textinput": @@ -645,7 +650,7 @@ export class WEWWA { visible = eval(cprop) == true; } catch (e) { - Smallog.Error("Error: (" + cprop + ") for: " + p + " => " + e); + Smallog.Error("Error: (" + cprop + ") for: " + p + " => " + e, LogHead); } } @@ -708,7 +713,7 @@ export class WEWWA { this.source.connect(this.analyser); this.startAudioInterval(); }).catch((err) => { - Smallog.Error(err); + Smallog.Error(err, LogHead); if (location.protocol != "https:") { var r = confirm("Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS for this feature to work! Press 'ok'/'yes' to get redirected to HTTPS and try again."); if (r) window.location.href = window.location.href.replace("http", "https"); @@ -761,7 +766,7 @@ export class WEWWA { this.audioInterval = setInterval(() => { if (this.audioCallback == null) { this.stopAudioInterval(); - alert("no AudioCallback!"); + Smallog.Error("no AudioCallback!", LogHead); return; } this.analyser.getByteFrequencyData(data); diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 3c964e0..688185f 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -24,7 +24,7 @@ export class WarnHelper { }); } - injectCSS() { + private injectCSS() { var st = document.createElement("style"); st.innerHTML = ` #triggerwarn { @@ -41,7 +41,7 @@ export class WarnHelper { document.head.append(st); } - injectHTML() { + private injectHTML() { var outer = document.createElement("img"); outer.id = "triggerwarn"; outer.setAttribute("src", ` @@ -577,7 +577,7 @@ B/x9OcNpH2VDkjgAAAABJRU5ErkJggg== document.body.append(outer); } - Show(callback) { + public Show(callback) { // show it $("#triggerwarn").addClass("show"); // wait some time From 4c0d1c77b5f8e6ecb67a67bd657e1e043c4142f4 Mon Sep 17 00:00:00 2001 From: hexxone Date: Sat, 19 Dec 2020 00:49:07 +0100 Subject: [PATCH 12/76] Add AssemblyScript --- .gitignore | 120 + src/WEICUE.ts | 2 +- src/WEWWA.ts | 7 +- src/WarnHelper.ts | 5 + src/{ => offline}/OfflineHelper.ts | 12 +- src/{ => offline}/OfflinePlugin.js | 6 +- src/{ => offline}/OfflineWorker.ts | 3 +- src/wasm/WasmPlugin.js | 127 + src/wasm/asconfig.json | 20 + src/wasm/assembly/index.ts | 252 ++ src/wasm/assembly/tsconfig.json | 6 + src/wasm/build/optimized.wat | 3657 ++++++++++++++++++ src/wasm/build/untouched.wat | 5664 ++++++++++++++++++++++++++++ src/wasm/index.js | 5 + src/wasm/package-lock.json | 33 + src/wasm/package.json | 14 + src/wasm/tests/index.js | 4 + src/{ => weas}/WEAS.ts | 94 +- src/weas/WEAS.wasm.asc | 252 ++ src/{ => weas}/WEASWorker.ts | 0 20 files changed, 10233 insertions(+), 50 deletions(-) create mode 100644 .gitignore rename src/{ => offline}/OfflineHelper.ts (76%) rename src/{ => offline}/OfflinePlugin.js (96%) rename src/{ => offline}/OfflineWorker.ts (99%) create mode 100644 src/wasm/WasmPlugin.js create mode 100644 src/wasm/asconfig.json create mode 100644 src/wasm/assembly/index.ts create mode 100644 src/wasm/assembly/tsconfig.json create mode 100644 src/wasm/build/optimized.wat create mode 100644 src/wasm/build/untouched.wat create mode 100644 src/wasm/index.js create mode 100644 src/wasm/package-lock.json create mode 100644 src/wasm/package.json create mode 100644 src/wasm/tests/index.js rename src/{ => weas}/WEAS.ts (57%) create mode 100644 src/weas/WEAS.wasm.asc rename src/{ => weas}/WEASWorker.ts (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5944df3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,120 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +*.wasm +*.wasm.map +*.asm.js diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 63d86eb..fa126f9 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -22,7 +22,7 @@ import { CComponent } from "./CComponent"; import { CSettings } from "./CSettings"; import { Ready } from "./Ready"; import { Smallog } from "./Smallog"; -import { WEAS } from "./WEAS"; +import { WEAS } from "./weas/WEAS"; const ClassName: string = "[WEICUE] "; const canvasX: number = 23; diff --git a/src/WEWWA.ts b/src/WEWWA.ts index a4fe695..a436f2d 100644 --- a/src/WEWWA.ts +++ b/src/WEWWA.ts @@ -42,7 +42,7 @@ import { Ready } from "./Ready"; import { Smallog } from "./Smallog"; -import { OfflineHelper } from "./OfflineHelper"; +import { OfflineHelper } from "./offline/OfflineHelper"; import { CC } from "cookieconsent"; const LogHead = "[WEWWA] "; @@ -256,6 +256,7 @@ export class WEWWA { header.innerHTML = "

" + proj.title + "

"; // create workshop link var link = ce("a"); + link.setAttribute("rel", "noreferrer"); link.setAttribute("href", "https://steamcommunity.com/sharedfiles/filedetails/?id=" + proj.workshopid); link.setAttribute("target", "_blank"); link.innerHTML = "

Open Workshop Page

"; @@ -351,7 +352,7 @@ export class WEWWA { // add html struct var row = ce("tr"); var td1 = ce("td"); - td1.innerHTML = "

🇩🇪🇬🇧🇮🇹🇷🇺🇨🇳

"; + td1.innerHTML = "

🇩🇪🇬🇧🇮🇹🇷🇺🇨🇳

"; var td2 = ce("td"); var lan = ce("select"); // process all @@ -421,7 +422,7 @@ export class WEWWA { // footer with ident var footer = ce("div"); - footer.innerHTML = "

[W] allpaper
[E] ngine
[W] eb
[W] allpaper
[A] dapterby hexxone"; + footer.innerHTML = "

[W] allpaper
[E] ngine
[W] eb
[W] allpaper
[A] dapterby hexxone"; // finish up menu menu.append(preview, header, link, tmain, preFoot, footer) diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 688185f..f260efa 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -8,6 +8,11 @@ * * @description * Displays a seizure warning image centered on html for a given Time. + * + * @todo + * - add trigger warn languages to project json + * - add trigger warn as html + * - Remove embedded image */ import { Ready } from "./Ready"; diff --git a/src/OfflineHelper.ts b/src/offline/OfflineHelper.ts similarity index 76% rename from src/OfflineHelper.ts rename to src/offline/OfflineHelper.ts index ee8630a..b405f81 100644 --- a/src/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -16,7 +16,7 @@ * ServiceWorker is a progressive technology. Some browsers will be unsupported... */ -import { Smallog } from "./Smallog"; +import { Smallog } from "../Smallog"; // this should pack the serviceworker like a webwoker. import OfflineWorker from 'worker-loader!./OfflineWorker'; @@ -28,10 +28,15 @@ export module OfflineHelper { // function helper, so OfflineWorker is actually processed function DontRemove() { return new OfflineWorker() }; - export function Register(workerPath: string = "js/OfflineWorker.worker.js?jsonPath=/js/offlinefiles.json") { + // In order to intercept ALL fetch-requests offline, the scope "/" (root) is required. + // when you put in in a sub-directory like "/js/", the scope is also "/js/". + // Then, your HTTP Server will have to send the REPONSE HEADER `service-worker-allowed: /` + // otherwise it will cause an ERROR in your Browser. So: Putting the ServiceWorker in root folder is easiest. + // obviously with webpack, this causes a problem, when you are not outputting directly into the root dir... + export function Register(workerPath: string = "OfflineWorker.worker.js?jsonPath=offlinefiles.json") { return new Promise(async (resolve) => { if ('serviceWorker' in navigator) { - await navigator.serviceWorker.register(workerPath) + await navigator.serviceWorker.register(workerPath, { scope: "/" }) .then(() => Smallog.Info('service-worker registration complete.', LogHead), () => Smallog.Error('service-worker registration failure.', LogHead)) .then(() => resolve(true)); @@ -44,6 +49,7 @@ export module OfflineHelper { }); } + // unregister all service workers export async function Reset() { return new Promise((resolve) => { if ('serviceWorker' in navigator) { diff --git a/src/OfflinePlugin.js b/src/offline/OfflinePlugin.js similarity index 96% rename from src/OfflinePlugin.js rename to src/offline/OfflinePlugin.js index 19efc8b..110f604 100644 --- a/src/OfflinePlugin.js +++ b/src/offline/OfflinePlugin.js @@ -35,7 +35,7 @@ const pluginName = 'OfflinePlugin'; const schema = { type: 'object', properties: { - outdir: { + staticdir: { type: 'string' }, outfile: { @@ -90,7 +90,7 @@ class OfflinePlugin { var filelist = []; // add static files from folder - var sFiles = getAllFiles(this.options.outdir, ""); + var sFiles = getAllFiles(this.options.staticdir, ""); for (var staticFile in sFiles) { filelist.push(sFiles[staticFile]); } @@ -98,7 +98,7 @@ class OfflinePlugin { // Loop through all compiled assets, // adding a new line item for each filename. for (var filename in compilation.assets) { - filelist.push('/' + filename); + filelist.push('/'+ filename); } // add additional files anyway? diff --git a/src/OfflineWorker.ts b/src/offline/OfflineWorker.ts similarity index 99% rename from src/OfflineWorker.ts rename to src/offline/OfflineWorker.ts index 1b01b80..451cc8a 100644 --- a/src/OfflineWorker.ts +++ b/src/offline/OfflineWorker.ts @@ -58,7 +58,8 @@ wrk.addEventListener("install", function (event: any) { // comprehends even the request for the HTML page on first load, as well as JS and // CSS resources, fonts, any images, etc. wrk.addEventListener("fetch", function (event: any) { - console.info(wName + 'fetch event in progress.'); + //console.info(wName + 'fetch event in progress.'); + if (event.request.method !== 'GET') { console.info(wName + 'fetch event ignored.', event.request.method, event.request.url); return; diff --git a/src/wasm/WasmPlugin.js b/src/wasm/WasmPlugin.js new file mode 100644 index 0000000..528de04 --- /dev/null +++ b/src/wasm/WasmPlugin.js @@ -0,0 +1,127 @@ +/** + * + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * This is a webpack plugin + * + */ + + +const fs = require("fs"); +const path = require('path'); + +const validate = require('schema-utils'); +const { RawSource } = require('webpack-sources'); +const { execSync } = require('child_process'); + +const pluginName = 'WasmPlugin'; +const sourcePath = path.resolve(__dirname, 'assembly/index.ts'); +const targetPath = path.resolve(__dirname, 'build/optimized.wasm'); + +// schema for options object +const schema = { + type: 'object', + properties: { + relpath: { + type: 'string' + }, + regexx: { + type: 'object' + } + } +}; + +// list files recursively +function getAllFiles(baseDir, subDir, arrayOfFiles) { + var sub = baseDir + "/" + subDir; + var files = fs.readdirSync(sub); + var arrayOfFiles = arrayOfFiles || []; + files.forEach((file) => { + var fle = subDir + "/" + file; + if (fs.statSync(sub + "/" + file).isDirectory()) + arrayOfFiles = getAllFiles(baseDir, fle, arrayOfFiles); + else + arrayOfFiles.push(fle); + }); + return arrayOfFiles; +} + + +// compile assemblyscript (typescript) module to wasm and return binary +function compileWasm(inputPath) { + return new Promise((resolve) => { + // copy .wasm.ts -> index.ts + // File destination.txt will be created or overwritten by default. + fs.copyFile(inputPath, sourcePath, (err) => { + // check for and catch errors + try { + if (err) throw err; + let output = execSync('npm run asbuild', { cwd: __dirname }); + console.log("Compile result: " + output); + // none? -> read and resolve optimized.wasm string + var binary = fs.readFileSync(targetPath); + resolve(binary); + } + catch(ex) { + console.warn(pluginName + " Compile Error!"); + console.error(ex); + } + }); + }); + +} + +// actual webpack plugin +class WasmPlugin { + + options = {}; + + constructor(options = {}) { + validate.validate(schema, options); + this.options = options; + } + + // Define `apply` as its prototype method which is supplied with compiler as its argument + apply(compiler) { + var addedOnce = false; + // Specify the event hook to attach to + compiler.hooks.thisCompilation.tap(pluginName, + (compilation) => compilation.hooks.processAssets.tap({ + name: pluginName, + stage: -2000, + }, async () => { + if (addedOnce) return; + addedOnce = true; + + console.log('This is an experimental plugin!'); + + // add static files from folder + const rPath = path.resolve(__dirname, this.options.relpath); + var sFiles = getAllFiles(rPath, ""); + for (var staticFile in sFiles) { + const sFile = sFiles[staticFile]; + const sName = sFile.replace(/^.*[\\\/]/, ''); + // @TODO if regex match wasm name, compile + if (sName.match(this.options.regexx)) { + console.info("Compile wasm: " + sName); + const cWasm = await compileWasm(rPath + sFile); + // @todo keep ".wasm" and remove ".ts" part of name + const newName = sName.replace(/\.[^/.]+$/, ""); + console.info("Success! File: " + newName); + compilation.emitAsset(newName, new RawSource(cWasm)); + } + } + + console.info("[" + pluginName + "] successfull!"); + }) + ); + } +} + +module.exports = WasmPlugin; \ No newline at end of file diff --git a/src/wasm/asconfig.json b/src/wasm/asconfig.json new file mode 100644 index 0000000..6405405 --- /dev/null +++ b/src/wasm/asconfig.json @@ -0,0 +1,20 @@ +{ + "targets": { + "debug": { + "binaryFile": "build/untouched.wasm", + "textFile": "build/untouched.wat", + "sourceMap": true, + "debug": true + }, + "release": { + "binaryFile": "build/optimized.wasm", + "textFile": "build/optimized.wat", + "sourceMap": true, + "optimizeLevel": 3, + "shrinkLevel": 1, + "converge": false, + "noAssert": false + } + }, + "options": {} +} \ No newline at end of file diff --git a/src/wasm/assembly/index.ts b/src/wasm/assembly/index.ts new file mode 100644 index 0000000..dc25421 --- /dev/null +++ b/src/wasm/assembly/index.ts @@ -0,0 +1,252 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Wallaper Engine Audio Supplier worker. + */ + +const DAT_LEN = 128; +const HLF_LEN = 64; +const QRT_LEN = 32; + +const pinkNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, + 0.5452348949654, 0.50723325864167, 0.4677726234682, 0.44204182748767, 0.41956517802157, + 0.41517375040002, 0.41312118577934, 0.40618363960446, 0.39913707474975, 0.38207008614508, + 0.38329789106488, 0.37472136606245, 0.36586428412968, 0.37603017335105, 0.39762590761573, + 0.39391828858591, 0.37930603769622, 0.39433365764563, 0.38511504613859, 0.39082579241834, + 0.3811852720504, 0.40231453727161, 0.40244151133175, 0.39965366884521, 0.39761103827545, + 0.51136400422212, 0.66151212038954, 0.66312205226679, 0.7416276690995, 0.74614971301133, + 0.84797007577483, 0.8573583910469, 0.96382997811663, 0.99819377577185, 1.0628692615814, + 1.1059083969751, 1.1819808497335, 1.257092297208, 1.3226521464753, 1.3735992532905, + 1.4953223705889, 1.5310064942373, 1.6193923584808, 1.7094805527135, 1.7706604552218, + 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, + 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, + 2.4043566176474, 2.4280476777842, 2.3917477397336, 2.4032522546622, 2.3614180150678]; + +// correct pink noise for first and second half +function correctPinkNoise(data: Float64Array): void { + var correct = new Float64Array(DAT_LEN); + for (var i = 0; i < HLF_LEN; i++) { + correct[i] = data[i] / pinkNoise[i]; + correct[HLF_LEN + i] = data[HLF_LEN + i] / pinkNoise[i]; + } + data.set(correct); +} + +// merge first and second half into full range +function stereoToMono(data: Float64Array): void { + var mono = new Float64Array(DAT_LEN); + var mIdx = 0; + for (var i = 0; i < HLF_LEN; i++) { + mono[mIdx++] = data[i]; + mono[mIdx++] = data[HLF_LEN + i]; + } + data.set(mono); +} + +// switch front with back in first half +function invertFirst(data: Float64Array): void { + for (var i = 0; i < QRT_LEN; i++) { + var a = data[i]; + data[i] = data[HLF_LEN - i]; + data[HLF_LEN - i] = a; + } +} + +// switch front with back in second half +function invertSecond(data: Float64Array): void { + for (var i = 0; i < QRT_LEN; i++) { + var b = data[HLF_LEN + i]; + data[HLF_LEN + i] = data[DAT_LEN - i]; + data[DAT_LEN - i] = b; + } +} + +// switch front with back in full range +function invertAll(data: Float64Array): void { + for (var i = 0; i < HLF_LEN; i++) { + var a = data[i]; + data[i] = data[DAT_LEN - i]; + data[DAT_LEN - i] = a; + } +} + +// filter peaks for full range +function peakFilter(array: Float64Array, amount: f64): void { + var oldMax: f64 = 0; + var newMax: f64 = 0; + var newArray = new Float64Array(DAT_LEN); + var i = 0; + // pow this shit + for (; i < DAT_LEN; i++) { + if (array[i] > oldMax) oldMax = array[i]; + newArray[i] = Math.pow(array[i] * amount, amount) as f64; + if (newArray[i] > newMax) newMax = newArray[i]; + } + // re-scale & apply + var divide: f64 = newMax / oldMax; + for (i = 0; i < DAT_LEN; i++) + array[i] = newArray[i] / divide; +} + +// smooth values for full range +function smoothArray(array: Float64Array, steps: i32): void { + var newArray = new Float64Array(DAT_LEN); + // make smoothed array + for (var outer = 0; outer < DAT_LEN; outer++) { + var sum: f64 = 0; + for (var inner = outer - steps; inner <= outer + steps; inner++) { + var idx = inner; + if (idx < 0) idx += DAT_LEN; + sum += array[idx % DAT_LEN]; + } + newArray[outer] = sum / (((steps * 2) + 1) as f64); + } + array.set(newArray); +} + +// function will apply setting-defined data smoothing +function applyValueLeveling(curr: Float64Array, prev: Float64Array, inc: f64, dec: f64): void { + for (var i = 0; i < DAT_LEN; i++) { + var diff: f64 = curr[i] - prev[i]; + var mlt: f64 = 100 - (diff > 0 ? inc : dec); + curr[i] -= diff * mlt / 100; + } +} + +// this will hold the current processed audio data +// either: B-H | H-B | HL-BL-BR-HR | BL-HL-HR-BR +// where ( B=bass, H=high, L=left, R=right ) +// in range > 0.0 and ~< 1.5 +export const audioData = new Float64Array(DAT_LEN); + +// this will hold the current processed properties: +export const audioProps = new Float64Array(10); +enum Props { + bass = 0, + mids = 1, + highs = 2, + sum = 3, + min = 4, + max = 5, + average = 6, + range = 7, + silent = 8, + intensity = 9 +} + + +// this will hold the current processing settings +export const audioSettings = new Float64Array(8); +enum Sett { + equalize = 0, + mono_audio = 1, + audio_direction = 2, + peak_filter = 3, + value_smoothing = 4, + audio_increase = 5, + audio_decrease = 6, + minimum_volume = 7 +} + +// check helper +function isOn(a: f64): boolean { + return a > 0; +} + +// this will update and process new data +export function update(newData: Float64Array): void { + + // fix pink noise? + if (isOn(audioSettings[Sett.equalize])) + correctPinkNoise(newData); + + // write botch channels to mono + if (isOn(audioSettings[Sett.mono_audio])) + stereoToMono(newData); + + if (isOn(audioSettings[Sett.audio_direction])) { + // flipped high & low mapping + if (isOn(audioSettings[Sett.mono_audio])) + // flip whole range + invertAll(newData); + else { + // only flip first half of stereo + invertFirst(newData); + } + + } + else { + // normal high & low mapping + if (isOn(audioSettings[Sett.mono_audio])) { + // only flip the second half of the data + invertSecond(newData); + } + } + + // process peaks? + if (isOn(audioSettings[Sett.peak_filter])) + peakFilter(newData, audioSettings[Sett.peak_filter] + 1); + + // smooth data? + if (isOn(audioSettings[Sett.value_smoothing])) + smoothArray(newData, audioSettings[Sett.value_smoothing] as i32); + + // process with last data? + if (audioData) + applyValueLeveling(newData, audioData, + audioSettings[Sett.audio_increase], + audioSettings[Sett.audio_decrease]); + + // process current frequency data and previous if given + var sum: f64 = 0, + min: f64 = 1, + max: f64 = 0, + bass: f64 = 0, + mids: f64 = 0, + peaks: f64 = 0; + + for (var indx = 0; indx < DAT_LEN; indx++) { + // parse current freq value + var idata: f64 = newData[indx]; + // process min max value + if (idata < min) min = idata; + if (idata > max) max = idata; + // process ranges + if (indx < (42 / 3)) bass += idata * audioProps[Props.bass]; + else if (indx > 69) peaks += idata * audioProps[Props.highs]; + else mids += idata * audioProps[Props.mids]; + // calc peak average + sum += idata; + } + + // calc average with previous entry + var average: f64 = sum / (DAT_LEN as f64); + var silent: f64 = (max < audioSettings[Sett.minimum_volume] / 1000) ? 0.9999 : 0.00; + var intensity: f64 = (bass * 8 - mids + peaks) / 6 / average; + var range: f64 = max - min; + + // Apply Data + audioData.set(newData); + audioProps.set([ + bass, + mids, + peaks, + sum, + min, + max, + average, + range, + silent, + intensity + ]); + + // clean + gc.collect(); +} + diff --git a/src/wasm/assembly/tsconfig.json b/src/wasm/assembly/tsconfig.json new file mode 100644 index 0000000..e28fcf2 --- /dev/null +++ b/src/wasm/assembly/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": [ + "./**/*.ts" + ] +} \ No newline at end of file diff --git a/src/wasm/build/optimized.wat b/src/wasm/build/optimized.wat new file mode 100644 index 0000000..44281e9 --- /dev/null +++ b/src/wasm/build/optimized.wat @@ -0,0 +1,3657 @@ +(module + (type $i32_i32_=>_none (func (param i32 i32))) + (type $i32_=>_none (func (param i32))) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) + (type $none_=>_none (func)) + (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $i32_i32_f64_=>_none (func (param i32 i32 f64))) + (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) + (type $i32_=>_f64 (func (param i32) (result f64))) + (type $i32_i32_=>_f64 (func (param i32 i32) (result f64))) + (type $f64_f64_=>_f64 (func (param f64 f64) (result f64))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (memory $0 1) + (data (i32.const 1036) "(\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00(\00\00\00a\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e") + (data (i32.const 1100) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00p\00u\00r\00e\00.\00t\00s") + (data (i32.const 1164) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") + (data (i32.const 1229) "\02\00\00\01") + (data (i32.const 1245) "\02\00\00Z>v\e8\0b\d1\f2?yY\f9B0D\eb?\0d\94\96\8c\92\07\e6?Xd\18\d8\ddg\e4?\deuPs\90r\e1?G\e7)>A;\e0?\83\a4\f0\95\fc\ef\dd?\ef\82!\ceiJ\dc?\ea\'\89\e7\'\da\da?V\0c\08\ec4\92\da?\94?\8d\d7\93p\da?\b8^\11\aa\e9\fe\d9?P\f6\aa:v\8b\d9?.y1\17\d6s\d8?\aa\f6\af\e0\f3\87\d8?Tq\16So\fb\d7?\a0!\c7\07Rj\d7?\af\866\dc\e0\10\d8?\c4\1eP\ef\b3r\d9?\1fu\b1\0d\f55\d9?\af(\c5\d4\8cF\d8?\e03\d3<\c3<\d9?\b57\17\94\b9\a5\d8?\fb\ae7/J\03\d9?\da\17K\e9Ve\d8?1]\12y\85\bf\d9?b@n\n\9a\c1\d9?m\a9Z\fb\ec\93\d9?\83\fcz\91ur\d9?!\8aO\0b\18]\e0?\cea_w\1b+\e5?\a4\c0\f7\bcK8\e5?\fb\e9\12\f3i\bb\e7?\d9\b5\e9\\u\e0\e7?\af\10\ee#\92\"\eb?v\ecO\ddzo\eb?PJ]\f7\b1\d7\ee?\e8X\c0\124\f1\ef?\0b\a7\e62\83\01\f1?\d3\13\d6\00\cd\b1\f1?\a5\a8a\c0d\e9\f2?n\fd\08\d0\0c\1d\f4?\a4\87\11L\95)\f5?_\15\eb5C\fa\f5?\19\81j&\d7\ec\f7?\b2gk\aa\00\7f\f8?\d8\131\f6\07\e9\f9?\fc\d6\b0G\08Z\fb?;\f8\b7\0e\a0T\fc?\ddU\8ayQ\96\fd?\ff\ea\a7m\0e\c8\fe?U\12\b5\c1\ff\1c\00@*\d10\8e\0f\a1\00@^[I\c1\aaB\01@\0e\aa\dbH\d0\c1\01@pJ\ba\81\ca \02@\aa\a5\eb\c1\eb\90\02@m\t\11\01!\dc\02@\85\84\0d\e8[0\03@\ec\bb\85R\1f<\03@\f1\b1\c9B\a4l\03@z\80\93\a3L\"\03@\d2\7fnQ\dc9\03@$6\d7 /\e4\02@") + (data (i32.const 1772) "\10\00\00\00\01\00\00\00\00\00\00\00\03\00\00\00\10\00\00\00\e0\04\00\00\e0\04\00\00\00\02\00\00@") + (data (i32.const 1820) "\1c\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1c\00\00\00I\00n\00v\00a\00l\00i\00d\00 \00l\00e\00n\00g\00t\00h") + (data (i32.const 1868) "&\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00&\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00b\00u\00f\00f\00e\00r\00.\00t\00s") + (data (i32.const 1932) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e") + (data (i32.const 1996) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00~\00l\00i\00b\00/\00t\00y\00p\00e\00d\00a\00r\00r\00a\00y\00.\00t\00s") + (data (i32.const 2060) "\1a\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s") + (data (i32.const 2112) "\07\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\"\1a\00\00\00\00\00\00!\1a\00\00\02\00\00\00a\00\00\00\02\00\00\00\"\t") + (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) + (global $assembly/index/audioData (mut i32) (i32.const 0)) + (global $assembly/index/audioProps (mut i32) (i32.const 0)) + (global $assembly/index/audioSettings (mut i32) (i32.const 0)) + (global $~lib/rt/__rtti_base i32 (i32.const 2112)) + (export "memory" (memory $0)) + (export "__new" (func $~lib/rt/pure/__new)) + (export "__renew" (func $~lib/rt/pure/__renew)) + (export "__retain" (func $~lib/rt/pure/__retain)) + (export "__release" (func $~lib/rt/pure/__release)) + (export "__collect" (func $~lib/rt/pure/__collect)) + (export "__rtti_base" (global $~lib/rt/__rtti_base)) + (export "audioData" (global $assembly/index/audioData)) + (export "audioProps" (global $assembly/index/audioProps)) + (export "audioSettings" (global $assembly/index/audioSettings)) + (export "update" (func $assembly/index/update)) + (start $~start) + (func $~lib/rt/tlsf/removeBlock (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $1 + i32.load + local.tee $2 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 272 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $2 + i32.const -4 + i32.and + local.tee $2 + i32.const 1073741820 + i32.lt_u + i32.const 0 + local.get $2 + i32.const 12 + i32.ge_u + select + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 274 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $2 + i32.const 256 + i32.lt_u + if + local.get $2 + i32.const 4 + i32.shr_u + local.set $2 + else + local.get $2 + i32.const 31 + local.get $2 + i32.clz + i32.sub + local.tee $3 + i32.const 4 + i32.sub + i32.shr_u + i32.const 16 + i32.xor + local.set $2 + local.get $3 + i32.const 7 + i32.sub + local.set $3 + end + local.get $2 + i32.const 16 + i32.lt_u + i32.const 0 + local.get $3 + i32.const 23 + i32.lt_u + select + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 287 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.load offset=8 + local.set $4 + local.get $1 + i32.load offset=4 + local.tee $5 + if + local.get $5 + local.get $4 + i32.store offset=8 + end + local.get $4 + if + local.get $4 + local.get $5 + i32.store offset=4 + end + local.get $1 + local.get $0 + local.get $2 + local.get $3 + i32.const 4 + i32.shl + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + i32.eq + if + local.get $0 + local.get $2 + local.get $3 + i32.const 4 + i32.shl + i32.add + i32.const 2 + i32.shl + i32.add + local.get $4 + i32.store offset=96 + local.get $4 + i32.eqz + if + local.get $0 + local.get $3 + i32.const 2 + i32.shl + i32.add + local.tee $4 + i32.load offset=4 + i32.const -2 + local.get $2 + i32.rotl + i32.and + local.set $1 + local.get $4 + local.get $1 + i32.store offset=4 + local.get $1 + i32.eqz + if + local.get $0 + local.get $0 + i32.load + i32.const -2 + local.get $3 + i32.rotl + i32.and + i32.store + end + end + end + ) + (func $~lib/rt/tlsf/insertBlock (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + local.get $1 + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 200 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.load + local.tee $4 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 202 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 4 + i32.add + local.get $1 + i32.load + i32.const -4 + i32.and + i32.add + local.tee $5 + i32.load + local.tee $2 + i32.const 1 + i32.and + if + local.get $4 + i32.const -4 + i32.and + i32.const 4 + i32.add + local.get $2 + i32.const -4 + i32.and + i32.add + local.tee $3 + i32.const 1073741820 + i32.lt_u + if + local.get $0 + local.get $5 + call $~lib/rt/tlsf/removeBlock + local.get $1 + local.get $3 + local.get $4 + i32.const 3 + i32.and + i32.or + local.tee $4 + i32.store + local.get $1 + i32.const 4 + i32.add + local.get $1 + i32.load + i32.const -4 + i32.and + i32.add + local.tee $5 + i32.load + local.set $2 + end + end + local.get $4 + i32.const 2 + i32.and + if + local.get $1 + i32.const 4 + i32.sub + i32.load + local.tee $3 + i32.load + local.tee $7 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 223 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $7 + i32.const -4 + i32.and + i32.const 4 + i32.add + local.get $4 + i32.const -4 + i32.and + i32.add + local.tee $8 + i32.const 1073741820 + i32.lt_u + if (result i32) + local.get $0 + local.get $3 + call $~lib/rt/tlsf/removeBlock + local.get $3 + local.get $8 + local.get $7 + i32.const 3 + i32.and + i32.or + local.tee $4 + i32.store + local.get $3 + else + local.get $1 + end + local.set $1 + end + local.get $5 + local.get $2 + i32.const 2 + i32.or + i32.store + local.get $4 + i32.const -4 + i32.and + local.tee $3 + i32.const 1073741820 + i32.lt_u + i32.const 0 + local.get $3 + i32.const 12 + i32.ge_u + select + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 238 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $3 + local.get $1 + i32.const 4 + i32.add + i32.add + local.get $5 + i32.ne + if + i32.const 0 + i32.const 1184 + i32.const 239 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $5 + i32.const 4 + i32.sub + local.get $1 + i32.store + local.get $3 + i32.const 256 + i32.lt_u + if + local.get $3 + i32.const 4 + i32.shr_u + local.set $3 + else + local.get $3 + i32.const 31 + local.get $3 + i32.clz + i32.sub + local.tee $4 + i32.const 4 + i32.sub + i32.shr_u + i32.const 16 + i32.xor + local.set $3 + local.get $4 + i32.const 7 + i32.sub + local.set $6 + end + local.get $3 + i32.const 16 + i32.lt_u + i32.const 0 + local.get $6 + i32.const 23 + i32.lt_u + select + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 255 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $3 + local.get $6 + i32.const 4 + i32.shl + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + local.set $4 + local.get $1 + i32.const 0 + i32.store offset=4 + local.get $1 + local.get $4 + i32.store offset=8 + local.get $4 + if + local.get $4 + local.get $1 + i32.store offset=4 + end + local.get $0 + local.get $3 + local.get $6 + i32.const 4 + i32.shl + i32.add + i32.const 2 + i32.shl + i32.add + local.get $1 + i32.store offset=96 + local.get $0 + local.get $0 + i32.load + i32.const 1 + local.get $6 + i32.shl + i32.or + i32.store + local.get $0 + local.get $6 + i32.const 2 + i32.shl + i32.add + local.tee $0 + local.get $0 + i32.load offset=4 + i32.const 1 + local.get $3 + i32.shl + i32.or + i32.store offset=4 + ) + (func $~lib/rt/tlsf/addMemory (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + local.get $1 + local.get $2 + i32.gt_u + if + i32.const 0 + i32.const 1184 + i32.const 380 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 19 + i32.add + i32.const -16 + i32.and + i32.const 4 + i32.sub + local.set $1 + local.get $2 + i32.const -16 + i32.and + local.get $0 + i32.load offset=1568 + local.tee $2 + if + local.get $1 + local.get $2 + i32.const 4 + i32.add + i32.lt_u + if + i32.const 0 + i32.const 1184 + i32.const 387 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $2 + local.get $1 + i32.const 16 + i32.sub + i32.eq + if + local.get $2 + i32.load + local.set $4 + local.get $1 + i32.const 16 + i32.sub + local.set $1 + end + else + local.get $1 + local.get $0 + i32.const 1572 + i32.add + i32.lt_u + if + i32.const 0 + i32.const 1184 + i32.const 400 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + end + local.get $1 + i32.sub + local.tee $2 + i32.const 20 + i32.lt_u + if + return + end + local.get $1 + local.get $4 + i32.const 2 + i32.and + local.get $2 + i32.const 8 + i32.sub + local.tee $2 + i32.const 1 + i32.or + i32.or + i32.store + local.get $1 + i32.const 0 + i32.store offset=4 + local.get $1 + i32.const 0 + i32.store offset=8 + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.add + local.tee $2 + i32.const 2 + i32.store + local.get $0 + local.get $2 + i32.store offset=1568 + local.get $0 + local.get $1 + call $~lib/rt/tlsf/insertBlock + ) + (func $~lib/rt/tlsf/initialize + (local $0 i32) + (local $1 i32) + memory.size + local.tee $0 + i32.const 1 + i32.lt_s + if (result i32) + i32.const 1 + local.get $0 + i32.sub + memory.grow + i32.const 0 + i32.lt_s + else + i32.const 0 + end + if + unreachable + end + i32.const 2176 + i32.const 0 + i32.store + i32.const 3744 + i32.const 0 + i32.store + loop $for-loop|0 + local.get $1 + i32.const 23 + i32.lt_u + if + local.get $1 + i32.const 2 + i32.shl + i32.const 2176 + i32.add + i32.const 0 + i32.store offset=4 + i32.const 0 + local.set $0 + loop $for-loop|1 + local.get $0 + i32.const 16 + i32.lt_u + if + local.get $0 + local.get $1 + i32.const 4 + i32.shl + i32.add + i32.const 2 + i32.shl + i32.const 2176 + i32.add + i32.const 0 + i32.store offset=96 + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|1 + end + end + local.get $1 + i32.const 1 + i32.add + local.set $1 + br $for-loop|0 + end + end + i32.const 2176 + i32.const 3748 + memory.size + i32.const 16 + i32.shl + call $~lib/rt/tlsf/addMemory + i32.const 2176 + global.set $~lib/rt/tlsf/ROOT + ) + (func $~lib/rt/tlsf/prepareSize (param $0 i32) (result i32) + local.get $0 + i32.const 1073741820 + i32.ge_u + if + i32.const 1056 + i32.const 1184 + i32.const 461 + i32.const 30 + call $~lib/builtins/abort + unreachable + end + i32.const 12 + local.get $0 + i32.const 19 + i32.add + i32.const -16 + i32.and + i32.const 4 + i32.sub + local.get $0 + i32.const 12 + i32.le_u + select + ) + (func $~lib/rt/tlsf/searchBlock (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + local.get $1 + i32.const 256 + i32.lt_u + if + local.get $1 + i32.const 4 + i32.shr_u + local.set $1 + else + i32.const 31 + local.get $1 + i32.const 1 + i32.const 27 + local.get $1 + i32.clz + i32.sub + i32.shl + i32.add + i32.const 1 + i32.sub + local.get $1 + local.get $1 + i32.const 536870910 + i32.lt_u + select + local.tee $1 + i32.clz + i32.sub + local.set $2 + local.get $1 + local.get $2 + i32.const 4 + i32.sub + i32.shr_u + i32.const 16 + i32.xor + local.set $1 + local.get $2 + i32.const 7 + i32.sub + local.set $2 + end + local.get $1 + i32.const 16 + i32.lt_u + i32.const 0 + local.get $2 + i32.const 23 + i32.lt_u + select + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 333 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $2 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + i32.const -1 + local.get $1 + i32.shl + i32.and + local.tee $1 + if (result i32) + local.get $0 + local.get $1 + i32.ctz + local.get $2 + i32.const 4 + i32.shl + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + else + local.get $0 + i32.load + i32.const -1 + local.get $2 + i32.const 1 + i32.add + i32.shl + i32.and + local.tee $1 + if (result i32) + local.get $0 + local.get $1 + i32.ctz + local.tee $1 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + local.tee $2 + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 346 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $2 + i32.ctz + local.get $1 + i32.const 4 + i32.shl + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + else + i32.const 0 + end + end + ) + (func $~lib/rt/tlsf/prepareBlock (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + local.get $1 + i32.load + local.set $3 + local.get $2 + i32.const 4 + i32.add + i32.const 15 + i32.and + if + i32.const 0 + i32.const 1184 + i32.const 360 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const -4 + i32.and + local.get $2 + i32.sub + local.tee $4 + i32.const 16 + i32.ge_u + if + local.get $1 + local.get $2 + local.get $3 + i32.const 2 + i32.and + i32.or + i32.store + local.get $2 + local.get $1 + i32.const 4 + i32.add + i32.add + local.tee $1 + local.get $4 + i32.const 4 + i32.sub + i32.const 1 + i32.or + i32.store + local.get $0 + local.get $1 + call $~lib/rt/tlsf/insertBlock + else + local.get $1 + local.get $3 + i32.const -2 + i32.and + i32.store + local.get $1 + i32.const 4 + i32.add + local.tee $0 + local.get $1 + i32.load + i32.const -4 + i32.and + i32.add + local.get $0 + local.get $1 + i32.load + i32.const -4 + i32.and + i32.add + i32.load + i32.const -3 + i32.and + i32.store + end + ) + (func $~lib/rt/tlsf/allocateBlock (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + local.get $0 + local.get $1 + call $~lib/rt/tlsf/prepareSize + local.tee $2 + call $~lib/rt/tlsf/searchBlock + local.tee $1 + i32.eqz + if + i32.const 4 + memory.size + local.tee $1 + i32.const 16 + i32.shl + i32.const 4 + i32.sub + local.get $0 + i32.load offset=1568 + i32.ne + i32.shl + local.get $2 + i32.const 1 + i32.const 27 + local.get $2 + i32.clz + i32.sub + i32.shl + i32.const 1 + i32.sub + i32.add + local.get $2 + local.get $2 + i32.const 536870910 + i32.lt_u + select + i32.add + i32.const 65535 + i32.add + i32.const -65536 + i32.and + i32.const 16 + i32.shr_u + local.set $3 + local.get $1 + local.get $3 + local.get $1 + local.get $3 + i32.gt_s + select + memory.grow + i32.const 0 + i32.lt_s + if + local.get $3 + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + local.get $0 + local.get $1 + i32.const 16 + i32.shl + memory.size + i32.const 16 + i32.shl + call $~lib/rt/tlsf/addMemory + local.get $0 + local.get $2 + call $~lib/rt/tlsf/searchBlock + local.tee $1 + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 498 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + end + local.get $2 + local.get $1 + i32.load + i32.const -4 + i32.and + i32.gt_u + if + i32.const 0 + i32.const 1184 + i32.const 500 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $~lib/rt/tlsf/removeBlock + local.get $0 + local.get $1 + local.get $2 + call $~lib/rt/tlsf/prepareBlock + local.get $1 + ) + (func $~lib/rt/pure/__new (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + local.get $0 + i32.const 1073741804 + i32.gt_u + if + i32.const 1056 + i32.const 1120 + i32.const 275 + i32.const 30 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 16 + i32.add + local.set $2 + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + global.get $~lib/rt/tlsf/ROOT + local.get $2 + call $~lib/rt/tlsf/allocateBlock + i32.const 4 + i32.add + local.tee $3 + i32.const 4 + i32.sub + local.tee $2 + i32.const 0 + i32.store offset=4 + local.get $2 + i32.const 0 + i32.store offset=8 + local.get $2 + local.get $1 + i32.store offset=12 + local.get $2 + local.get $0 + i32.store offset=16 + local.get $3 + i32.const 16 + i32.add + ) + (func $~lib/rt/tlsf/checkUsedBlock (param $0 i32) (result i32) + (local $1 i32) + local.get $0 + i32.const 4 + i32.sub + local.set $1 + local.get $0 + i32.const 15 + i32.and + i32.eqz + i32.const 0 + local.get $0 + select + if (result i32) + local.get $1 + i32.load + i32.const 1 + i32.and + i32.eqz + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 1184 + i32.const 563 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $1 + ) + (func $~lib/memory/memory.copy (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + block $~lib/util/memory/memmove|inlined.0 + local.get $2 + local.set $4 + local.get $0 + local.get $1 + i32.eq + br_if $~lib/util/memory/memmove|inlined.0 + local.get $0 + local.get $1 + i32.lt_u + if + local.get $1 + i32.const 7 + i32.and + local.get $0 + i32.const 7 + i32.and + i32.eq + if + loop $while-continue|0 + local.get $0 + i32.const 7 + i32.and + if + local.get $4 + i32.eqz + br_if $~lib/util/memory/memmove|inlined.0 + local.get $4 + i32.const 1 + i32.sub + local.set $4 + local.get $0 + local.tee $2 + i32.const 1 + i32.add + local.set $0 + local.get $1 + local.tee $3 + i32.const 1 + i32.add + local.set $1 + local.get $2 + local.get $3 + i32.load8_u + i32.store8 + br $while-continue|0 + end + end + loop $while-continue|1 + local.get $4 + i32.const 8 + i32.ge_u + if + local.get $0 + local.get $1 + i64.load + i64.store + local.get $4 + i32.const 8 + i32.sub + local.set $4 + local.get $0 + i32.const 8 + i32.add + local.set $0 + local.get $1 + i32.const 8 + i32.add + local.set $1 + br $while-continue|1 + end + end + end + loop $while-continue|2 + local.get $4 + if + local.get $0 + local.tee $2 + i32.const 1 + i32.add + local.set $0 + local.get $1 + local.tee $3 + i32.const 1 + i32.add + local.set $1 + local.get $2 + local.get $3 + i32.load8_u + i32.store8 + local.get $4 + i32.const 1 + i32.sub + local.set $4 + br $while-continue|2 + end + end + else + local.get $1 + i32.const 7 + i32.and + local.get $0 + i32.const 7 + i32.and + i32.eq + if + loop $while-continue|3 + local.get $0 + local.get $4 + i32.add + i32.const 7 + i32.and + if + local.get $4 + i32.eqz + br_if $~lib/util/memory/memmove|inlined.0 + local.get $4 + i32.const 1 + i32.sub + local.tee $4 + local.get $0 + i32.add + local.get $1 + local.get $4 + i32.add + i32.load8_u + i32.store8 + br $while-continue|3 + end + end + loop $while-continue|4 + local.get $4 + i32.const 8 + i32.ge_u + if + local.get $4 + i32.const 8 + i32.sub + local.tee $4 + local.get $0 + i32.add + local.get $1 + local.get $4 + i32.add + i64.load + i64.store + br $while-continue|4 + end + end + end + loop $while-continue|5 + local.get $4 + if + local.get $4 + i32.const 1 + i32.sub + local.tee $4 + local.get $0 + i32.add + local.get $1 + local.get $4 + i32.add + i32.load8_u + i32.store8 + br $while-continue|5 + end + end + end + end + ) + (func $~lib/rt/tlsf/freeBlock (param $0 i32) (param $1 i32) + local.get $1 + local.get $1 + i32.load + i32.const 1 + i32.or + i32.store + local.get $0 + local.get $1 + call $~lib/rt/tlsf/insertBlock + ) + (func $~lib/rt/tlsf/moveBlock (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + local.get $0 + local.get $2 + call $~lib/rt/tlsf/allocateBlock + local.tee $2 + i32.const 4 + i32.add + local.get $1 + i32.const 4 + i32.add + local.get $1 + i32.load + i32.const -4 + i32.and + call $~lib/memory/memory.copy + local.get $1 + i32.const 2172 + i32.ge_u + if + local.get $0 + local.get $1 + call $~lib/rt/tlsf/freeBlock + end + local.get $2 + ) + (func $~lib/rt/pure/__renew (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + local.get $1 + i32.const 1073741804 + i32.gt_u + if + i32.const 1056 + i32.const 1120 + i32.const 288 + i32.const 30 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 16 + i32.sub + local.set $0 + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + local.get $1 + i32.const 16 + i32.add + local.set $2 + local.get $0 + i32.const 2172 + i32.lt_u + if + global.get $~lib/rt/tlsf/ROOT + local.get $0 + call $~lib/rt/tlsf/checkUsedBlock + local.get $2 + call $~lib/rt/tlsf/moveBlock + local.set $0 + else + block $__inlined_func$~lib/rt/tlsf/reallocateBlock + global.get $~lib/rt/tlsf/ROOT + local.set $3 + local.get $0 + call $~lib/rt/tlsf/checkUsedBlock + local.set $0 + block $folding-inner0 + local.get $2 + call $~lib/rt/tlsf/prepareSize + local.tee $5 + local.get $0 + i32.load + local.tee $6 + i32.const -4 + i32.and + local.tee $4 + i32.le_u + br_if $folding-inner0 + local.get $0 + i32.const 4 + i32.add + local.get $0 + i32.load + i32.const -4 + i32.and + i32.add + local.tee $7 + i32.load + local.tee $8 + i32.const 1 + i32.and + if + local.get $5 + local.get $4 + i32.const 4 + i32.add + local.get $8 + i32.const -4 + i32.and + i32.add + local.tee $4 + i32.le_u + if + local.get $3 + local.get $7 + call $~lib/rt/tlsf/removeBlock + local.get $0 + local.get $4 + local.get $6 + i32.const 3 + i32.and + i32.or + i32.store + br $folding-inner0 + end + end + local.get $3 + local.get $0 + local.get $2 + call $~lib/rt/tlsf/moveBlock + local.set $0 + br $__inlined_func$~lib/rt/tlsf/reallocateBlock + end + local.get $3 + local.get $0 + local.get $5 + call $~lib/rt/tlsf/prepareBlock + end + end + local.get $0 + i32.const 4 + i32.add + local.tee $0 + i32.const 4 + i32.sub + local.get $1 + i32.store offset=16 + local.get $0 + i32.const 16 + i32.add + ) + (func $~lib/rt/pure/__retain (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + local.get $0 + i32.const 2172 + i32.gt_u + if + local.get $0 + i32.const 20 + i32.sub + local.tee $1 + i32.load offset=4 + local.tee $2 + i32.const -268435456 + i32.and + local.get $2 + i32.const 1 + i32.add + i32.const -268435456 + i32.and + i32.ne + if + i32.const 0 + i32.const 1120 + i32.const 109 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $1 + local.get $2 + i32.const 1 + i32.add + i32.store offset=4 + local.get $1 + i32.load + i32.const 1 + i32.and + if + i32.const 0 + i32.const 1120 + i32.const 112 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + end + local.get $0 + ) + (func $~lib/rt/pure/__release (param $0 i32) + local.get $0 + i32.const 2172 + i32.gt_u + if + local.get $0 + i32.const 20 + i32.sub + call $~lib/rt/pure/decrement + end + ) + (func $~lib/memory/memory.fill (param $0 i32) (param $1 i32) + (local $2 i32) + block $~lib/util/memory/memset|inlined.0 + local.get $1 + i32.eqz + br_if $~lib/util/memory/memset|inlined.0 + local.get $0 + i32.const 0 + i32.store8 + local.get $0 + local.get $1 + i32.add + i32.const 4 + i32.sub + local.tee $2 + i32.const 0 + i32.store8 offset=3 + local.get $1 + i32.const 2 + i32.le_u + br_if $~lib/util/memory/memset|inlined.0 + local.get $0 + i32.const 0 + i32.store8 offset=1 + local.get $0 + i32.const 0 + i32.store8 offset=2 + local.get $2 + i32.const 0 + i32.store8 offset=2 + local.get $2 + i32.const 0 + i32.store8 offset=1 + local.get $1 + i32.const 6 + i32.le_u + br_if $~lib/util/memory/memset|inlined.0 + local.get $0 + i32.const 0 + i32.store8 offset=3 + local.get $2 + i32.const 0 + i32.store8 + local.get $1 + i32.const 8 + i32.le_u + br_if $~lib/util/memory/memset|inlined.0 + local.get $0 + i32.const 0 + local.get $0 + i32.sub + i32.const 3 + i32.and + local.tee $2 + i32.add + local.tee $0 + i32.const 0 + i32.store + local.get $0 + local.get $1 + local.get $2 + i32.sub + i32.const -4 + i32.and + local.tee $2 + i32.add + i32.const 28 + i32.sub + local.tee $1 + i32.const 0 + i32.store offset=24 + local.get $2 + i32.const 8 + i32.le_u + br_if $~lib/util/memory/memset|inlined.0 + local.get $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store offset=8 + local.get $1 + i32.const 0 + i32.store offset=16 + local.get $1 + i32.const 0 + i32.store offset=20 + local.get $2 + i32.const 24 + i32.le_u + br_if $~lib/util/memory/memset|inlined.0 + local.get $0 + i32.const 0 + i32.store offset=12 + local.get $0 + i32.const 0 + i32.store offset=16 + local.get $0 + i32.const 0 + i32.store offset=20 + local.get $0 + i32.const 0 + i32.store offset=24 + local.get $1 + i32.const 0 + i32.store + local.get $1 + i32.const 0 + i32.store offset=4 + local.get $1 + i32.const 0 + i32.store offset=8 + local.get $1 + i32.const 0 + i32.store offset=12 + local.get $0 + local.get $0 + i32.const 4 + i32.and + i32.const 24 + i32.add + local.tee $1 + i32.add + local.set $0 + local.get $2 + local.get $1 + i32.sub + local.set $1 + loop $while-continue|0 + local.get $1 + i32.const 32 + i32.ge_u + if + local.get $0 + i64.const 0 + i64.store + local.get $0 + i64.const 0 + i64.store offset=8 + local.get $0 + i64.const 0 + i64.store offset=16 + local.get $0 + i64.const 0 + i64.store offset=24 + local.get $1 + i32.const 32 + i32.sub + local.set $1 + local.get $0 + i32.const 32 + i32.add + local.set $0 + br $while-continue|0 + end + end + end + ) + (func $~lib/typedarray/Float64Array#constructor (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + i32.const 12 + i32.const 4 + call $~lib/rt/pure/__new + call $~lib/rt/pure/__retain + local.tee $1 + i32.eqz + if + i32.const 12 + i32.const 2 + call $~lib/rt/pure/__new + call $~lib/rt/pure/__retain + local.set $1 + end + local.get $1 + i32.const 0 + i32.store + local.get $1 + i32.const 0 + i32.store offset=4 + local.get $1 + i32.const 0 + i32.store offset=8 + local.get $0 + i32.const 134217727 + i32.gt_u + if + i32.const 1840 + i32.const 1888 + i32.const 18 + i32.const 57 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 3 + i32.shl + local.tee $3 + i32.const 0 + call $~lib/rt/pure/__new + local.tee $0 + local.get $3 + call $~lib/memory/memory.fill + local.get $0 + local.set $2 + local.get $0 + local.get $1 + i32.load + local.tee $4 + i32.ne + if + local.get $2 + call $~lib/rt/pure/__retain + local.set $2 + local.get $4 + call $~lib/rt/pure/__release + end + local.get $1 + local.get $2 + i32.store + local.get $1 + local.get $0 + i32.store offset=4 + local.get $1 + local.get $3 + i32.store offset=8 + local.get $1 + ) + (func $~lib/typedarray/Float64Array#__get (param $0 i32) (param $1 i32) (result f64) + local.get $1 + local.get $0 + i32.load offset=8 + i32.const 3 + i32.shr_u + i32.ge_u + if + i32.const 1952 + i32.const 2016 + i32.const 1304 + i32.const 64 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=4 + local.get $1 + i32.const 3 + i32.shl + i32.add + f64.load + ) + (func $~lib/array/Array#__get (param $0 i32) (result f64) + local.get $0 + i32.const 1804 + i32.load + i32.ge_u + if + i32.const 1952 + i32.const 2080 + i32.const 104 + i32.const 42 + call $~lib/builtins/abort + unreachable + end + i32.const 1796 + i32.load + local.get $0 + i32.const 3 + i32.shl + i32.add + f64.load + ) + (func $~lib/typedarray/Float64Array#__set (param $0 i32) (param $1 i32) (param $2 f64) + local.get $1 + local.get $0 + i32.load offset=8 + i32.const 3 + i32.shr_u + i32.ge_u + if + i32.const 1952 + i32.const 2016 + i32.const 1315 + i32.const 64 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=4 + local.get $1 + i32.const 3 + i32.shl + i32.add + local.get $2 + f64.store + ) + (func $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> (param $0 i32) (param $1 i32) + (local $2 i32) + local.get $1 + call $~lib/rt/pure/__retain + local.set $2 + local.get $0 + call $~lib/rt/pure/__retain + local.set $0 + local.get $2 + call $~lib/rt/pure/__retain + local.tee $1 + i32.load offset=8 + i32.const 3 + i32.shr_u + local.get $0 + i32.load offset=8 + i32.const 3 + i32.shr_u + i32.gt_s + if + i32.const 1952 + i32.const 2016 + i32.const 1775 + i32.const 47 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=4 + local.get $1 + i32.load offset=4 + local.get $1 + i32.load offset=8 + call $~lib/memory/memory.copy + local.get $1 + call $~lib/rt/pure/__release + local.get $0 + call $~lib/rt/pure/__release + local.get $2 + call $~lib/rt/pure/__release + ) + (func $~lib/math/NativeMath.pow (param $0 f64) (param $1 f64) (result f64) + (local $2 i32) + (local $3 i32) + (local $4 f64) + (local $5 i32) + (local $6 i32) + (local $7 f64) + (local $8 f64) + (local $9 f64) + (local $10 f64) + (local $11 i64) + (local $12 i32) + (local $13 i32) + (local $14 i32) + (local $15 i32) + (local $16 f64) + (local $17 i32) + (local $18 f64) + (local $19 f64) + block $folding-inner3 + block $folding-inner2 + block $folding-inner1 + block $folding-inner0 + local.get $1 + f64.abs + f64.const 2 + f64.le + if + local.get $1 + f64.const 2 + f64.eq + br_if $folding-inner0 + local.get $1 + f64.const 0.5 + f64.eq + if + local.get $0 + f64.sqrt + f64.abs + f64.const inf + local.get $0 + f64.const -inf + f64.ne + select + return + end + local.get $1 + f64.const -1 + f64.eq + br_if $folding-inner1 + local.get $1 + f64.const 1 + f64.eq + if + local.get $0 + return + end + local.get $1 + f64.const 0 + f64.eq + if + f64.const 1 + return + end + end + local.get $0 + i64.reinterpret_f64 + local.tee $11 + i32.wrap_i64 + local.set $13 + local.get $11 + i64.const 32 + i64.shr_u + i32.wrap_i64 + local.tee $12 + i32.const 2147483647 + i32.and + local.set $5 + local.get $1 + i64.reinterpret_f64 + local.tee $11 + i64.const 32 + i64.shr_u + i32.wrap_i64 + local.tee $3 + i32.const 2147483647 + i32.and + local.tee $6 + local.get $11 + i32.wrap_i64 + local.tee $14 + i32.or + i32.eqz + if + f64.const 1 + return + end + i32.const 1 + local.get $14 + i32.const 0 + local.get $6 + i32.const 2146435072 + i32.eq + select + i32.const 1 + local.get $6 + i32.const 2146435072 + i32.gt_u + i32.const 1 + local.get $13 + i32.const 0 + local.get $5 + i32.const 2146435072 + i32.eq + select + local.get $5 + i32.const 2146435072 + i32.gt_s + select + select + select + if + local.get $0 + local.get $1 + f64.add + return + end + local.get $12 + i32.const 0 + i32.lt_s + if (result i32) + local.get $6 + i32.const 1128267776 + i32.ge_u + if (result i32) + i32.const 2 + else + local.get $6 + i32.const 1072693248 + i32.ge_u + if (result i32) + i32.const 2 + local.get $14 + local.get $6 + local.get $6 + i32.const 20 + i32.shr_u + i32.const 1023 + i32.sub + local.tee $2 + i32.const 20 + i32.gt_s + local.tee $15 + select + local.tee $17 + i32.const 52 + i32.const 20 + local.get $15 + select + local.get $2 + i32.sub + local.tee $2 + i32.shr_u + local.tee $15 + i32.const 1 + i32.and + i32.sub + i32.const 0 + local.get $17 + local.get $15 + local.get $2 + i32.shl + i32.eq + select + else + i32.const 0 + end + end + else + i32.const 0 + end + local.set $2 + local.get $14 + i32.eqz + if + local.get $6 + i32.const 2146435072 + i32.eq + if + local.get $13 + local.get $5 + i32.const 1072693248 + i32.sub + i32.or + if + local.get $5 + i32.const 1072693248 + i32.ge_s + if + local.get $1 + f64.const 0 + local.get $3 + i32.const 0 + i32.ge_s + select + return + else + f64.const 0 + local.get $1 + f64.neg + local.get $3 + i32.const 0 + i32.ge_s + select + return + end + unreachable + else + f64.const nan:0x8000000000000 + return + end + unreachable + end + local.get $6 + i32.const 1072693248 + i32.eq + if + local.get $3 + i32.const 0 + i32.ge_s + if + local.get $0 + return + end + br $folding-inner1 + end + local.get $3 + i32.const 1073741824 + i32.eq + br_if $folding-inner0 + local.get $3 + i32.const 1071644672 + i32.eq + if + local.get $12 + i32.const 0 + i32.ge_s + if + local.get $0 + f64.sqrt + return + end + end + end + local.get $0 + f64.abs + local.set $4 + local.get $13 + i32.eqz + if + i32.const 1 + local.get $5 + i32.const 1072693248 + i32.eq + local.get $5 + i32.const 2146435072 + i32.eq + i32.const 1 + local.get $5 + select + select + if + f64.const 1 + local.get $4 + f64.div + local.get $4 + local.get $3 + i32.const 0 + i32.lt_s + select + local.set $0 + local.get $12 + i32.const 0 + i32.lt_s + if (result f64) + local.get $2 + local.get $5 + i32.const 1072693248 + i32.sub + i32.or + if (result f64) + local.get $0 + f64.neg + local.get $0 + local.get $2 + i32.const 1 + i32.eq + select + else + local.get $0 + local.get $0 + f64.sub + local.tee $0 + local.get $0 + f64.div + end + else + local.get $0 + end + return + end + end + local.get $12 + i32.const 0 + i32.lt_s + if (result f64) + local.get $2 + i32.eqz + if + local.get $0 + local.get $0 + f64.sub + local.tee $0 + local.get $0 + f64.div + return + end + f64.const -1 + f64.const 1 + local.get $2 + i32.const 1 + i32.eq + select + else + f64.const 1 + end + local.set $8 + local.get $6 + i32.const 1105199104 + i32.gt_u + if (result f64) + local.get $6 + i32.const 1139802112 + i32.gt_u + if + local.get $5 + i32.const 1072693247 + i32.le_s + if + f64.const inf + f64.const 0 + local.get $3 + i32.const 0 + i32.lt_s + select + return + end + local.get $5 + i32.const 1072693248 + i32.ge_s + if + f64.const inf + f64.const 0 + local.get $3 + i32.const 0 + i32.gt_s + select + return + end + end + local.get $5 + i32.const 1072693247 + i32.lt_s + if + local.get $8 + f64.const 1.e+300 + f64.mul + f64.const 1.e+300 + f64.mul + local.get $8 + f64.const 1e-300 + f64.mul + f64.const 1e-300 + f64.mul + local.get $3 + i32.const 0 + i32.lt_s + select + return + end + local.get $5 + i32.const 1072693248 + i32.gt_s + if + local.get $8 + f64.const 1.e+300 + f64.mul + f64.const 1.e+300 + f64.mul + local.get $8 + f64.const 1e-300 + f64.mul + f64.const 1e-300 + f64.mul + local.get $3 + i32.const 0 + i32.gt_s + select + return + end + local.get $4 + f64.const 1 + f64.sub + local.tee $0 + f64.const 1.4426950216293335 + f64.mul + local.tee $4 + local.get $0 + f64.const 1.9259629911266175e-08 + f64.mul + local.get $0 + local.get $0 + f64.mul + f64.const 0.5 + local.get $0 + f64.const 0.3333333333333333 + local.get $0 + f64.const 0.25 + f64.mul + f64.sub + f64.mul + f64.sub + f64.mul + f64.const 1.4426950408889634 + f64.mul + f64.sub + local.tee $7 + f64.add + i64.reinterpret_f64 + i64.const -4294967296 + i64.and + f64.reinterpret_i64 + local.set $0 + local.get $7 + local.get $0 + local.get $4 + f64.sub + f64.sub + else + local.get $5 + i32.const 1048576 + i32.lt_s + if (result i32) + local.get $4 + f64.const 9007199254740992 + f64.mul + local.tee $4 + i64.reinterpret_f64 + i64.const 32 + i64.shr_u + i32.wrap_i64 + local.set $5 + i32.const -53 + else + i32.const 0 + end + local.get $5 + i32.const 20 + i32.shr_s + i32.const 1023 + i32.sub + i32.add + local.set $3 + local.get $5 + i32.const 1048575 + i32.and + local.tee $2 + i32.const 1072693248 + i32.or + local.set $5 + local.get $2 + i32.const 235662 + i32.le_s + if (result i32) + i32.const 0 + else + local.get $2 + i32.const 767610 + i32.lt_s + if (result i32) + i32.const 1 + else + local.get $3 + i32.const 1 + i32.add + local.set $3 + local.get $5 + i32.const -1048576 + i32.add + local.set $5 + i32.const 0 + end + end + local.set $2 + local.get $4 + i64.reinterpret_f64 + i64.const 4294967295 + i64.and + local.get $5 + i64.extend_i32_s + i64.const 32 + i64.shl + i64.or + f64.reinterpret_i64 + local.tee $9 + f64.const 1.5 + f64.const 1 + local.get $2 + select + local.tee $10 + f64.sub + local.tee $18 + f64.const 1 + local.get $9 + local.get $10 + f64.add + f64.div + local.tee $19 + f64.mul + local.tee $7 + i64.reinterpret_f64 + i64.const -4294967296 + i64.and + f64.reinterpret_i64 + local.tee $4 + local.get $4 + f64.mul + local.set $16 + local.get $4 + local.get $16 + f64.const 3 + f64.add + local.get $7 + local.get $7 + f64.mul + local.tee $0 + local.get $0 + f64.mul + local.get $0 + local.get $0 + local.get $0 + local.get $0 + local.get $0 + f64.const 0.20697501780033842 + f64.mul + f64.const 0.23066074577556175 + f64.add + f64.mul + f64.const 0.272728123808534 + f64.add + f64.mul + f64.const 0.33333332981837743 + f64.add + f64.mul + f64.const 0.4285714285785502 + f64.add + f64.mul + f64.const 0.5999999999999946 + f64.add + f64.mul + local.get $19 + local.get $18 + local.get $4 + local.get $5 + i32.const 1 + i32.shr_s + i32.const 536870912 + i32.or + i32.const 524288 + i32.add + local.get $2 + i32.const 18 + i32.shl + i32.add + i64.extend_i32_s + i64.const 32 + i64.shl + f64.reinterpret_i64 + local.tee $0 + f64.mul + f64.sub + local.get $4 + local.get $9 + local.get $0 + local.get $10 + f64.sub + f64.sub + f64.mul + f64.sub + f64.mul + local.tee $9 + local.get $4 + local.get $7 + f64.add + f64.mul + f64.add + local.tee $4 + f64.add + i64.reinterpret_f64 + i64.const -4294967296 + i64.and + f64.reinterpret_i64 + local.tee $0 + f64.mul + local.tee $10 + local.get $9 + local.get $0 + f64.mul + local.get $4 + local.get $0 + f64.const 3 + f64.sub + local.get $16 + f64.sub + f64.sub + local.get $7 + f64.mul + f64.add + local.tee $4 + f64.add + i64.reinterpret_f64 + i64.const -4294967296 + i64.and + f64.reinterpret_i64 + local.tee $0 + f64.const 0.9617967009544373 + f64.mul + local.tee $7 + local.get $0 + f64.const -7.028461650952758e-09 + f64.mul + local.get $4 + local.get $0 + local.get $10 + f64.sub + f64.sub + f64.const 0.9617966939259756 + f64.mul + f64.add + f64.const 1.350039202129749e-08 + f64.const 0 + local.get $2 + select + f64.add + local.tee $4 + f64.add + f64.const 0.5849624872207642 + f64.const 0 + local.get $2 + select + local.tee $9 + f64.add + local.get $3 + f64.convert_i32_s + local.tee $10 + f64.add + i64.reinterpret_f64 + i64.const -4294967296 + i64.and + f64.reinterpret_i64 + local.set $0 + local.get $4 + local.get $0 + local.get $10 + f64.sub + local.get $9 + f64.sub + local.get $7 + f64.sub + f64.sub + end + local.set $4 + local.get $1 + local.get $1 + i64.reinterpret_f64 + i64.const -4294967296 + i64.and + f64.reinterpret_i64 + local.tee $7 + f64.sub + local.get $0 + f64.mul + local.get $1 + local.get $4 + f64.mul + f64.add + local.tee $1 + local.get $7 + local.get $0 + f64.mul + local.tee $0 + f64.add + local.tee $4 + i64.reinterpret_f64 + local.tee $11 + i32.wrap_i64 + local.set $3 + local.get $11 + i64.const 32 + i64.shr_u + i32.wrap_i64 + local.tee $2 + i32.const 1083179008 + i32.ge_s + if + local.get $3 + local.get $2 + i32.const 1083179008 + i32.sub + i32.or + local.get $1 + f64.const 8.008566259537294e-17 + f64.add + local.get $4 + local.get $0 + f64.sub + f64.gt + i32.or + br_if $folding-inner2 + else + local.get $2 + i32.const 2147483647 + i32.and + i32.const 1083231232 + i32.ge_u + i32.const 0 + local.get $3 + local.get $2 + i32.const -1064252416 + i32.sub + i32.or + local.get $1 + local.get $4 + local.get $0 + f64.sub + f64.le + i32.or + select + br_if $folding-inner3 + end + local.get $2 + i32.const 2147483647 + i32.and + local.tee $5 + i32.const 20 + i32.shr_u + i32.const 1023 + i32.sub + local.set $6 + i32.const 0 + local.set $3 + local.get $1 + local.get $5 + i32.const 1071644672 + i32.gt_s + if + local.get $2 + i32.const 1048576 + local.get $6 + i32.const 1 + i32.add + i32.shr_s + i32.add + local.tee $5 + i32.const 2147483647 + i32.and + i32.const 20 + i32.shr_u + i32.const 1023 + i32.sub + local.set $6 + i32.const 0 + local.get $5 + i32.const 1048575 + i32.and + i32.const 1048576 + i32.or + i32.const 20 + local.get $6 + i32.sub + i32.shr_s + local.tee $3 + i32.sub + local.get $3 + local.get $2 + i32.const 0 + i32.lt_s + select + local.set $3 + local.get $0 + local.get $5 + i32.const 1048575 + local.get $6 + i32.shr_s + i32.const -1 + i32.xor + i32.and + i64.extend_i32_s + i64.const 32 + i64.shl + f64.reinterpret_i64 + f64.sub + local.set $0 + end + local.get $0 + f64.add + i64.reinterpret_f64 + i64.const -4294967296 + i64.and + f64.reinterpret_i64 + local.tee $4 + f64.const 0.6931471824645996 + f64.mul + local.tee $7 + local.get $1 + local.get $4 + local.get $0 + f64.sub + f64.sub + f64.const 0.6931471805599453 + f64.mul + local.get $4 + f64.const -1.904654299957768e-09 + f64.mul + f64.add + local.tee $4 + f64.add + local.tee $0 + local.get $0 + f64.mul + local.set $1 + local.get $8 + f64.const 1 + local.get $0 + local.get $0 + local.get $1 + local.get $1 + local.get $1 + local.get $1 + local.get $1 + f64.const 4.1381367970572385e-08 + f64.mul + f64.const -1.6533902205465252e-06 + f64.add + f64.mul + f64.const 6.613756321437934e-05 + f64.add + f64.mul + f64.const -2.7777777777015593e-03 + f64.add + f64.mul + f64.const 0.16666666666666602 + f64.add + f64.mul + f64.sub + local.tee $1 + f64.mul + local.get $1 + f64.const 2 + f64.sub + f64.div + local.get $4 + local.get $0 + local.get $7 + f64.sub + f64.sub + local.tee $1 + local.get $0 + local.get $1 + f64.mul + f64.add + f64.sub + local.get $0 + f64.sub + f64.sub + local.tee $0 + i64.reinterpret_f64 + i64.const 32 + i64.shr_u + i32.wrap_i64 + local.get $3 + i32.const 20 + i32.shl + i32.add + local.tee $2 + i32.const 20 + i32.shr_s + i32.const 0 + i32.le_s + if (result f64) + local.get $3 + local.tee $2 + i32.const 1023 + i32.gt_s + if (result f64) + local.get $0 + f64.const 8988465674311579538646525e283 + f64.mul + local.set $0 + local.get $2 + i32.const 1023 + i32.sub + local.tee $2 + i32.const 1023 + i32.gt_s + if (result f64) + local.get $2 + i32.const 1023 + i32.sub + local.tee $2 + i32.const 1023 + local.get $2 + i32.const 1023 + i32.lt_s + select + local.set $2 + local.get $0 + f64.const 8988465674311579538646525e283 + f64.mul + else + local.get $0 + end + else + local.get $2 + i32.const -1022 + i32.lt_s + if (result f64) + local.get $0 + f64.const 2.004168360008973e-292 + f64.mul + local.set $0 + local.get $2 + i32.const 969 + i32.add + local.tee $2 + i32.const -1022 + i32.lt_s + if (result f64) + local.get $2 + i32.const 969 + i32.add + local.tee $2 + i32.const -1022 + local.get $2 + i32.const -1022 + i32.gt_s + select + local.set $2 + local.get $0 + f64.const 2.004168360008973e-292 + f64.mul + else + local.get $0 + end + else + local.get $0 + end + end + local.get $2 + i64.extend_i32_s + i64.const 1023 + i64.add + i64.const 52 + i64.shl + f64.reinterpret_i64 + f64.mul + else + local.get $0 + i64.reinterpret_f64 + i64.const 4294967295 + i64.and + local.get $2 + i64.extend_i32_s + i64.const 32 + i64.shl + i64.or + f64.reinterpret_i64 + end + f64.mul + return + end + local.get $0 + local.get $0 + f64.mul + return + end + f64.const 1 + local.get $0 + f64.div + return + end + local.get $8 + f64.const 1.e+300 + f64.mul + f64.const 1.e+300 + f64.mul + return + end + local.get $8 + f64.const 1e-300 + f64.mul + f64.const 1e-300 + f64.mul + ) + (func $assembly/index/update (param $0 i32) + (local $1 f64) + (local $2 f64) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 f64) + (local $10 f64) + (local $11 f64) + (local $12 i32) + (local $13 f64) + (local $14 f64) + (local $15 f64) + local.get $0 + call $~lib/rt/pure/__retain + local.set $6 + global.get $assembly/index/audioSettings + i32.const 0 + call $~lib/typedarray/Float64Array#__get + f64.const 0 + f64.gt + if + i32.const 0 + local.set $0 + local.get $6 + call $~lib/rt/pure/__retain + local.set $4 + i32.const 128 + call $~lib/typedarray/Float64Array#constructor + local.set $5 + loop $for-loop|0 + local.get $0 + i32.const 64 + i32.lt_s + if + local.get $5 + local.get $0 + local.get $4 + local.get $0 + call $~lib/typedarray/Float64Array#__get + local.get $0 + call $~lib/array/Array#__get + f64.div + call $~lib/typedarray/Float64Array#__set + local.get $5 + local.get $0 + i32.const -64 + i32.sub + local.tee $7 + local.get $4 + local.get $7 + call $~lib/typedarray/Float64Array#__get + local.get $0 + call $~lib/array/Array#__get + f64.div + call $~lib/typedarray/Float64Array#__set + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|0 + end + end + local.get $4 + local.get $5 + call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> + local.get $4 + call $~lib/rt/pure/__release + local.get $5 + call $~lib/rt/pure/__release + end + global.get $assembly/index/audioSettings + i32.const 1 + call $~lib/typedarray/Float64Array#__get + f64.const 0 + f64.gt + if + i32.const 0 + local.set $0 + i32.const 0 + local.set $7 + local.get $6 + call $~lib/rt/pure/__retain + local.set $4 + i32.const 128 + call $~lib/typedarray/Float64Array#constructor + local.set $5 + loop $for-loop|01 + local.get $0 + i32.const 64 + i32.lt_s + if + local.get $5 + local.get $7 + local.get $4 + local.get $0 + call $~lib/typedarray/Float64Array#__get + call $~lib/typedarray/Float64Array#__set + local.get $7 + i32.const 1 + i32.add + local.tee $12 + i32.const 1 + i32.add + local.set $7 + local.get $5 + local.get $12 + local.get $4 + local.get $0 + i32.const -64 + i32.sub + call $~lib/typedarray/Float64Array#__get + call $~lib/typedarray/Float64Array#__set + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|01 + end + end + local.get $4 + local.get $5 + call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> + local.get $4 + call $~lib/rt/pure/__release + local.get $5 + call $~lib/rt/pure/__release + end + global.get $assembly/index/audioSettings + i32.const 2 + call $~lib/typedarray/Float64Array#__get + f64.const 0 + f64.gt + if + global.get $assembly/index/audioSettings + i32.const 1 + call $~lib/typedarray/Float64Array#__get + f64.const 0 + f64.gt + if + local.get $6 + call $~lib/rt/pure/__retain + local.set $0 + loop $for-loop|04 + local.get $3 + i32.const 64 + i32.lt_s + if + local.get $0 + local.get $3 + call $~lib/typedarray/Float64Array#__get + local.set $2 + local.get $0 + local.get $3 + local.get $0 + i32.const 128 + local.get $3 + i32.sub + local.tee $4 + call $~lib/typedarray/Float64Array#__get + call $~lib/typedarray/Float64Array#__set + local.get $0 + local.get $4 + local.get $2 + call $~lib/typedarray/Float64Array#__set + local.get $3 + i32.const 1 + i32.add + local.set $3 + br $for-loop|04 + end + end + local.get $0 + call $~lib/rt/pure/__release + else + local.get $6 + call $~lib/rt/pure/__retain + local.set $0 + loop $for-loop|00 + local.get $3 + i32.const 32 + i32.lt_s + if + local.get $0 + local.get $3 + call $~lib/typedarray/Float64Array#__get + local.set $2 + local.get $0 + local.get $3 + local.get $0 + i32.const 64 + local.get $3 + i32.sub + local.tee $4 + call $~lib/typedarray/Float64Array#__get + call $~lib/typedarray/Float64Array#__set + local.get $0 + local.get $4 + local.get $2 + call $~lib/typedarray/Float64Array#__set + local.get $3 + i32.const 1 + i32.add + local.set $3 + br $for-loop|00 + end + end + local.get $0 + call $~lib/rt/pure/__release + end + else + global.get $assembly/index/audioSettings + i32.const 1 + call $~lib/typedarray/Float64Array#__get + f64.const 0 + f64.gt + if + local.get $6 + call $~lib/rt/pure/__retain + local.set $0 + loop $for-loop|016 + local.get $3 + i32.const 32 + i32.lt_s + if + local.get $0 + local.get $3 + i32.const -64 + i32.sub + local.tee $4 + call $~lib/typedarray/Float64Array#__get + local.set $2 + local.get $0 + local.get $4 + local.get $0 + i32.const 128 + local.get $3 + i32.sub + local.tee $4 + call $~lib/typedarray/Float64Array#__get + call $~lib/typedarray/Float64Array#__set + local.get $0 + local.get $4 + local.get $2 + call $~lib/typedarray/Float64Array#__set + local.get $3 + i32.const 1 + i32.add + local.set $3 + br $for-loop|016 + end + end + local.get $0 + call $~lib/rt/pure/__release + end + end + global.get $assembly/index/audioSettings + i32.const 3 + call $~lib/typedarray/Float64Array#__get + f64.const 0 + f64.gt + if + global.get $assembly/index/audioSettings + i32.const 3 + call $~lib/typedarray/Float64Array#__get + f64.const 1 + f64.add + local.set $11 + i32.const 0 + local.set $0 + f64.const 0 + local.set $2 + local.get $6 + call $~lib/rt/pure/__retain + local.set $3 + i32.const 128 + call $~lib/typedarray/Float64Array#constructor + local.set $4 + loop $for-loop|08 + local.get $0 + i32.const 128 + i32.lt_s + if + local.get $3 + local.get $0 + call $~lib/typedarray/Float64Array#__get + local.get $2 + f64.gt + if + local.get $3 + local.get $0 + call $~lib/typedarray/Float64Array#__get + local.set $2 + end + local.get $4 + local.get $0 + local.get $3 + local.get $0 + call $~lib/typedarray/Float64Array#__get + local.get $11 + f64.mul + local.get $11 + call $~lib/math/NativeMath.pow + call $~lib/typedarray/Float64Array#__set + local.get $4 + local.get $0 + call $~lib/typedarray/Float64Array#__get + local.get $1 + f64.gt + if + local.get $4 + local.get $0 + call $~lib/typedarray/Float64Array#__get + local.set $1 + end + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|08 + end + end + local.get $1 + local.get $2 + f64.div + local.set $1 + i32.const 0 + local.set $0 + loop $for-loop|1 + local.get $0 + i32.const 128 + i32.lt_s + if + local.get $3 + local.get $0 + local.get $4 + local.get $0 + call $~lib/typedarray/Float64Array#__get + local.get $1 + f64.div + call $~lib/typedarray/Float64Array#__set + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|1 + end + end + local.get $3 + call $~lib/rt/pure/__release + local.get $4 + call $~lib/rt/pure/__release + end + global.get $assembly/index/audioSettings + i32.const 4 + call $~lib/typedarray/Float64Array#__get + f64.const 0 + f64.gt + if + global.get $assembly/index/audioSettings + i32.const 4 + call $~lib/typedarray/Float64Array#__get + i32.trunc_f64_s + local.set $4 + i32.const 0 + local.set $0 + local.get $6 + call $~lib/rt/pure/__retain + local.set $5 + i32.const 128 + call $~lib/typedarray/Float64Array#constructor + local.set $7 + loop $for-loop|010 + local.get $0 + i32.const 128 + i32.lt_s + if + f64.const 0 + local.set $2 + local.get $0 + local.get $4 + i32.sub + local.set $3 + local.get $0 + local.get $4 + i32.add + local.set $12 + loop $for-loop|111 + local.get $3 + local.get $12 + i32.le_s + if + local.get $2 + local.get $5 + local.get $3 + i32.const 128 + i32.add + local.get $3 + local.get $3 + i32.const 0 + i32.lt_s + select + i32.const 128 + i32.rem_s + call $~lib/typedarray/Float64Array#__get + f64.add + local.set $2 + local.get $3 + i32.const 1 + i32.add + local.set $3 + br $for-loop|111 + end + end + local.get $7 + local.get $0 + local.get $2 + local.get $4 + i32.const 1 + i32.shl + i32.const 1 + i32.add + f64.convert_i32_s + f64.div + call $~lib/typedarray/Float64Array#__set + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|010 + end + end + local.get $5 + local.get $7 + call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> + local.get $5 + call $~lib/rt/pure/__release + local.get $7 + call $~lib/rt/pure/__release + end + global.get $assembly/index/audioData + if + global.get $assembly/index/audioData + global.get $assembly/index/audioSettings + i32.const 5 + call $~lib/typedarray/Float64Array#__get + local.set $2 + global.get $assembly/index/audioSettings + i32.const 6 + call $~lib/typedarray/Float64Array#__get + local.set $11 + i32.const 0 + local.set $0 + local.get $6 + call $~lib/rt/pure/__retain + local.set $3 + call $~lib/rt/pure/__retain + local.set $4 + loop $for-loop|012 + local.get $0 + i32.const 128 + i32.lt_s + if + local.get $3 + local.get $0 + call $~lib/typedarray/Float64Array#__get + local.get $4 + local.get $0 + call $~lib/typedarray/Float64Array#__get + f64.sub + local.set $1 + local.get $3 + local.get $0 + local.get $3 + local.get $0 + call $~lib/typedarray/Float64Array#__get + local.get $1 + f64.const 100 + local.get $2 + local.get $11 + local.get $1 + f64.const 0 + f64.gt + select + f64.sub + f64.mul + f64.const 100 + f64.div + f64.sub + call $~lib/typedarray/Float64Array#__set + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|012 + end + end + local.get $3 + call $~lib/rt/pure/__release + local.get $4 + call $~lib/rt/pure/__release + end + f64.const 1 + local.set $1 + loop $for-loop|02 + local.get $8 + i32.const 128 + i32.lt_s + if + local.get $1 + local.get $6 + local.get $8 + call $~lib/typedarray/Float64Array#__get + local.tee $2 + f64.gt + if + local.get $2 + local.set $1 + end + local.get $2 + local.get $9 + local.get $2 + local.get $9 + f64.gt + select + local.set $9 + local.get $8 + i32.const 14 + i32.lt_s + if + local.get $13 + local.get $2 + global.get $assembly/index/audioProps + i32.const 0 + call $~lib/typedarray/Float64Array#__get + f64.mul + f64.add + local.set $13 + else + local.get $8 + i32.const 69 + i32.gt_s + if + local.get $14 + local.get $2 + global.get $assembly/index/audioProps + i32.const 2 + call $~lib/typedarray/Float64Array#__get + f64.mul + f64.add + local.set $14 + else + local.get $15 + local.get $2 + global.get $assembly/index/audioProps + i32.const 1 + call $~lib/typedarray/Float64Array#__get + f64.mul + f64.add + local.set $15 + end + end + local.get $10 + local.get $2 + f64.add + local.set $10 + local.get $8 + i32.const 1 + i32.add + local.set $8 + br $for-loop|02 + end + end + f64.const 0.9999 + f64.const 0 + local.get $9 + global.get $assembly/index/audioSettings + i32.const 7 + call $~lib/typedarray/Float64Array#__get + f64.const 1e3 + f64.div + f64.lt + select + local.set $2 + global.get $assembly/index/audioData + local.get $6 + call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> + global.get $assembly/index/audioProps + i32.const 16 + i32.const 3 + call $~lib/rt/pure/__new + local.tee $0 + i32.const 80 + i32.const 0 + call $~lib/rt/pure/__new + local.tee $4 + call $~lib/rt/pure/__retain + i32.store + local.get $0 + local.get $4 + i32.store offset=4 + local.get $0 + i32.const 80 + i32.store offset=8 + local.get $0 + i32.const 10 + i32.store offset=12 + local.get $0 + call $~lib/rt/pure/__retain + local.tee $4 + i32.load offset=4 + local.tee $0 + local.get $13 + f64.store + local.get $0 + local.get $15 + f64.store offset=8 + local.get $0 + local.get $14 + f64.store offset=16 + local.get $0 + local.get $10 + f64.store offset=24 + local.get $0 + local.get $1 + f64.store offset=32 + local.get $0 + local.get $9 + f64.store offset=40 + local.get $0 + local.get $10 + f64.const 0.0078125 + f64.mul + local.tee $10 + f64.store offset=48 + local.get $0 + local.get $9 + local.get $1 + f64.sub + f64.store offset=56 + local.get $0 + local.get $2 + f64.store offset=64 + local.get $0 + local.get $13 + f64.const 8 + f64.mul + local.get $15 + f64.sub + local.get $14 + f64.add + f64.const 6 + f64.div + local.get $10 + f64.div + f64.store offset=72 + local.get $4 + call $~lib/rt/pure/__retain + local.set $8 + call $~lib/rt/pure/__retain + local.set $0 + local.get $8 + call $~lib/rt/pure/__retain + local.tee $3 + i32.load offset=12 + local.get $0 + i32.load offset=8 + i32.const 3 + i32.shr_u + i32.gt_s + if + i32.const 1952 + i32.const 2016 + i32.const 1775 + i32.const 47 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=4 + local.get $3 + i32.load offset=4 + local.get $3 + i32.load offset=8 + call $~lib/memory/memory.copy + local.get $3 + call $~lib/rt/pure/__release + local.get $0 + call $~lib/rt/pure/__release + local.get $8 + call $~lib/rt/pure/__release + local.get $4 + call $~lib/rt/pure/__release + local.get $6 + call $~lib/rt/pure/__release + ) + (func $~start + i32.const 128 + call $~lib/typedarray/Float64Array#constructor + global.set $assembly/index/audioData + i32.const 10 + call $~lib/typedarray/Float64Array#constructor + global.set $assembly/index/audioProps + i32.const 8 + call $~lib/typedarray/Float64Array#constructor + global.set $assembly/index/audioSettings + ) + (func $~lib/rt/pure/decrement (param $0 i32) + (local $1 i32) + (local $2 i32) + local.get $0 + i32.load offset=4 + local.tee $2 + i32.const 268435455 + i32.and + local.set $1 + local.get $0 + i32.load + i32.const 1 + i32.and + if + i32.const 0 + i32.const 1120 + i32.const 122 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 1 + i32.eq + if + block $__inlined_func$~lib/rt/__visit_members + block $folding-inner2 + block $folding-inner1 + block $invalid + block $~lib/arraybuffer/ArrayBufferView + local.get $0 + i32.const 12 + i32.add + i32.load + br_table $__inlined_func$~lib/rt/__visit_members $__inlined_func$~lib/rt/__visit_members $~lib/arraybuffer/ArrayBufferView $folding-inner1 $folding-inner2 $folding-inner2 $folding-inner1 $invalid + end + local.get $0 + i32.load offset=20 + local.tee $1 + if + local.get $1 + call $~lib/rt/pure/__visit + end + br $__inlined_func$~lib/rt/__visit_members + end + unreachable + end + local.get $0 + i32.load offset=20 + call $~lib/rt/pure/__visit + br $__inlined_func$~lib/rt/__visit_members + end + local.get $0 + i32.load offset=20 + local.tee $1 + if + local.get $1 + call $~lib/rt/pure/__visit + end + end + local.get $2 + i32.const -2147483648 + i32.and + if + i32.const 0 + i32.const 1120 + i32.const 126 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/rt/tlsf/ROOT + local.get $0 + call $~lib/rt/tlsf/freeBlock + else + local.get $1 + i32.eqz + if + i32.const 0 + i32.const 1120 + i32.const 136 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + i32.const 1 + i32.sub + local.get $2 + i32.const -268435456 + i32.and + i32.or + i32.store offset=4 + end + ) + (func $~lib/rt/pure/__collect + nop + ) + (func $~lib/rt/pure/__visit (param $0 i32) + local.get $0 + i32.const 2172 + i32.lt_u + if + return + end + local.get $0 + i32.const 20 + i32.sub + call $~lib/rt/pure/decrement + ) +) diff --git a/src/wasm/build/untouched.wat b/src/wasm/build/untouched.wat new file mode 100644 index 0000000..cffafca --- /dev/null +++ b/src/wasm/build/untouched.wat @@ -0,0 +1,5664 @@ +(module + (type $i32_i32_=>_none (func (param i32 i32))) + (type $i32_=>_none (func (param i32))) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) + (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) + (type $none_=>_none (func)) + (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) + (type $i32_i32_=>_f64 (func (param i32 i32) (result f64))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $i32_i32_f64_=>_none (func (param i32 i32 f64))) + (type $i32_i32_f64_f64_=>_none (func (param i32 i32 f64 f64))) + (type $i32_f64_=>_none (func (param i32 f64))) + (type $i32_i32_i32_i32_=>_i32 (func (param i32 i32 i32 i32) (result i32))) + (type $f64_=>_i32 (func (param f64) (result i32))) + (type $f64_f64_=>_f64 (func (param f64 f64) (result f64))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (memory $0 1) + (data (i32.const 12) "(\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00(\00\00\00a\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e\00") + (data (i32.const 76) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00p\00u\00r\00e\00.\00t\00s\00") + (data (i32.const 140) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00") + (data (i32.const 204) "\00\02\00\00\01\00\00\00\00\00\00\00\00\00\00\00\00\02\00\00Z>v\e8\0b\d1\f2?yY\f9B0D\eb?\0d\94\96\8c\92\07\e6?Xd\18\d8\ddg\e4?\deuPs\90r\e1?G\e7)>A;\e0?\83\a4\f0\95\fc\ef\dd?\ef\82!\ceiJ\dc?\ea\'\89\e7\'\da\da?V\0c\08\ec4\92\da?\94?\8d\d7\93p\da?\b8^\11\aa\e9\fe\d9?P\f6\aa:v\8b\d9?.y1\17\d6s\d8?\aa\f6\af\e0\f3\87\d8?Tq\16So\fb\d7?\a0!\c7\07Rj\d7?\af\866\dc\e0\10\d8?\c4\1eP\ef\b3r\d9?\1fu\b1\0d\f55\d9?\af(\c5\d4\8cF\d8?\e03\d3<\c3<\d9?\b57\17\94\b9\a5\d8?\fb\ae7/J\03\d9?\da\17K\e9Ve\d8?1]\12y\85\bf\d9?b@n\n\9a\c1\d9?m\a9Z\fb\ec\93\d9?\83\fcz\91ur\d9?!\8aO\0b\18]\e0?\cea_w\1b+\e5?\a4\c0\f7\bcK8\e5?\fb\e9\12\f3i\bb\e7?\d9\b5\e9\\u\e0\e7?\af\10\ee#\92\"\eb?v\ecO\ddzo\eb?PJ]\f7\b1\d7\ee?\e8X\c0\124\f1\ef?\0b\a7\e62\83\01\f1?\d3\13\d6\00\cd\b1\f1?\a5\a8a\c0d\e9\f2?n\fd\08\d0\0c\1d\f4?\a4\87\11L\95)\f5?_\15\eb5C\fa\f5?\19\81j&\d7\ec\f7?\b2gk\aa\00\7f\f8?\d8\131\f6\07\e9\f9?\fc\d6\b0G\08Z\fb?;\f8\b7\0e\a0T\fc?\ddU\8ayQ\96\fd?\ff\ea\a7m\0e\c8\fe?U\12\b5\c1\ff\1c\00@*\d10\8e\0f\a1\00@^[I\c1\aaB\01@\0e\aa\dbH\d0\c1\01@pJ\ba\81\ca \02@\aa\a5\eb\c1\eb\90\02@m\t\11\01!\dc\02@\85\84\0d\e8[0\03@\ec\bb\85R\1f<\03@\f1\b1\c9B\a4l\03@z\80\93\a3L\"\03@\d2\7fnQ\dc9\03@$6\d7 /\e4\02@") + (data (i32.const 748) "\10\00\00\00\01\00\00\00\00\00\00\00\03\00\00\00\10\00\00\00\e0\00\00\00\e0\00\00\00\00\02\00\00@\00\00\00") + (data (i32.const 796) "\1c\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1c\00\00\00I\00n\00v\00a\00l\00i\00d\00 \00l\00e\00n\00g\00t\00h\00") + (data (i32.const 844) "&\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00&\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00b\00u\00f\00f\00e\00r\00.\00t\00s\00") + (data (i32.const 908) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00") + (data (i32.const 972) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00~\00l\00i\00b\00/\00t\00y\00p\00e\00d\00a\00r\00r\00a\00y\00.\00t\00s\00") + (data (i32.const 1036) "\1a\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s\00") + (data (i32.const 1088) "\00\00\00\00\00\a0\f6?\00\00\00\00\00\00\00\00\00\c8\b9\f2\82,\d6\bf\80V7($\b4\fa<\00\00\00\00\00\80\f6?\00\00\00\00\00\00\00\00\00\08X\bf\bd\d1\d5\bf \f7\e0\d8\08\a5\1c\bd\00\00\00\00\00`\f6?\00\00\00\00\00\00\00\00\00XE\17wv\d5\bfmP\b6\d5\a4b#\bd\00\00\00\00\00@\f6?\00\00\00\00\00\00\00\00\00\f8-\87\ad\1a\d5\bf\d5g\b0\9e\e4\84\e6\bc\00\00\00\00\00 \f6?\00\00\00\00\00\00\00\00\00xw\95_\be\d4\bf\e0>)\93i\1b\04\bd\00\00\00\00\00\00\f6?\00\00\00\00\00\00\00\00\00`\1c\c2\8ba\d4\bf\cc\84LH/\d8\13=\00\00\00\00\00\e0\f5?\00\00\00\00\00\00\00\00\00\a8\86\860\04\d4\bf:\0b\82\ed\f3B\dc<\00\00\00\00\00\c0\f5?\00\00\00\00\00\00\00\00\00HiUL\a6\d3\bf`\94Q\86\c6\b1 =\00\00\00\00\00\a0\f5?\00\00\00\00\00\00\00\00\00\80\98\9a\ddG\d3\bf\92\80\c5\d4MY%=\00\00\00\00\00\80\f5?\00\00\00\00\00\00\00\00\00 \e1\ba\e2\e8\d2\bf\d8+\b7\99\1e{&=\00\00\00\00\00`\f5?\00\00\00\00\00\00\00\00\00\88\de\13Z\89\d2\bf?\b0\cf\b6\14\ca\15=\00\00\00\00\00`\f5?\00\00\00\00\00\00\00\00\00\88\de\13Z\89\d2\bf?\b0\cf\b6\14\ca\15=\00\00\00\00\00@\f5?\00\00\00\00\00\00\00\00\00x\cf\fbA)\d2\bfv\daS($Z\16\bd\00\00\00\00\00 \f5?\00\00\00\00\00\00\00\00\00\98i\c1\98\c8\d1\bf\04T\e7h\bc\af\1f\bd\00\00\00\00\00\00\f5?\00\00\00\00\00\00\00\00\00\a8\ab\ab\\g\d1\bf\f0\a8\823\c6\1f\1f=\00\00\00\00\00\e0\f4?\00\00\00\00\00\00\00\00\00H\ae\f9\8b\05\d1\bffZ\05\fd\c4\a8&\bd\00\00\00\00\00\c0\f4?\00\00\00\00\00\00\00\00\00\90s\e2$\a3\d0\bf\0e\03\f4~\eek\0c\bd\00\00\00\00\00\a0\f4?\00\00\00\00\00\00\00\00\00\d0\b4\94%@\d0\bf\7f-\f4\9e\b86\f0\bc\00\00\00\00\00\a0\f4?\00\00\00\00\00\00\00\00\00\d0\b4\94%@\d0\bf\7f-\f4\9e\b86\f0\bc\00\00\00\00\00\80\f4?\00\00\00\00\00\00\00\00\00@^m\18\b9\cf\bf\87<\99\ab*W\0d=\00\00\00\00\00`\f4?\00\00\00\00\00\00\00\00\00`\dc\cb\ad\f0\ce\bf$\af\86\9c\b7&+=\00\00\00\00\00@\f4?\00\00\00\00\00\00\00\00\00\f0*n\07\'\ce\bf\10\ff?TO/\17\bd\00\00\00\00\00 \f4?\00\00\00\00\00\00\00\00\00\c0Ok!\\\cd\bf\1bh\ca\bb\91\ba!=\00\00\00\00\00\00\f4?\00\00\00\00\00\00\00\00\00\a0\9a\c7\f7\8f\cc\bf4\84\9fhOy\'=\00\00\00\00\00\00\f4?\00\00\00\00\00\00\00\00\00\a0\9a\c7\f7\8f\cc\bf4\84\9fhOy\'=\00\00\00\00\00\e0\f3?\00\00\00\00\00\00\00\00\00\90-t\86\c2\cb\bf\8f\b7\8b1\b0N\19=\00\00\00\00\00\c0\f3?\00\00\00\00\00\00\00\00\00\c0\80N\c9\f3\ca\bff\90\cd?cN\ba<\00\00\00\00\00\a0\f3?\00\00\00\00\00\00\00\00\00\b0\e2\1f\bc#\ca\bf\ea\c1F\dcd\8c%\bd\00\00\00\00\00\a0\f3?\00\00\00\00\00\00\00\00\00\b0\e2\1f\bc#\ca\bf\ea\c1F\dcd\8c%\bd\00\00\00\00\00\80\f3?\00\00\00\00\00\00\00\00\00P\f4\9cZR\c9\bf\e3\d4\c1\04\d9\d1*\bd\00\00\00\00\00`\f3?\00\00\00\00\00\00\00\00\00\d0 e\a0\7f\c8\bf\t\fa\db\7f\bf\bd+=\00\00\00\00\00@\f3?\00\00\00\00\00\00\00\00\00\e0\10\02\89\ab\c7\bfXJSr\90\db+=\00\00\00\00\00@\f3?\00\00\00\00\00\00\00\00\00\e0\10\02\89\ab\c7\bfXJSr\90\db+=\00\00\00\00\00 \f3?\00\00\00\00\00\00\00\00\00\d0\19\e7\0f\d6\c6\bff\e2\b2\a3j\e4\10\bd\00\00\00\00\00\00\f3?\00\00\00\00\00\00\00\00\00\90\a7p0\ff\c5\bf9P\10\9fC\9e\1e\bd\00\00\00\00\00\00\f3?\00\00\00\00\00\00\00\00\00\90\a7p0\ff\c5\bf9P\10\9fC\9e\1e\bd\00\00\00\00\00\e0\f2?\00\00\00\00\00\00\00\00\00\b0\a1\e3\e5&\c5\bf\8f[\07\90\8b\de \bd\00\00\00\00\00\c0\f2?\00\00\00\00\00\00\00\00\00\80\cbl+M\c4\bf\11\0e\bd\00\00\00\00\00\e0\ed?\00\00\00\00\00\00\00\00\00`F\d1;\97\b1?\9b\9e\0dV]2%\bd\00\00\00\00\00\a0\ed?\00\00\00\00\00\00\00\00\00\e0\d1\a7\f5\bd\b3?\d7N\db\a5^\c8,=\00\00\00\00\00`\ed?\00\00\00\00\00\00\00\00\00\a0\97MZ\e9\b5?\1e\1d]<\06i,\bd\00\00\00\00\00@\ed?\00\00\00\00\00\00\00\00\00\c0\ea\n\d3\00\b7?2\ed\9d\a9\8d\1e\ec<\00\00\00\00\00\00\ed?\00\00\00\00\00\00\00\00\00@Y]^3\b9?\daG\bd:\\\11#=\00\00\00\00\00\c0\ec?\00\00\00\00\00\00\00\00\00`\ad\8d\c8j\bb?\e5h\f7+\80\90\13\bd\00\00\00\00\00\a0\ec?\00\00\00\00\00\00\00\00\00@\bc\01X\88\bc?\d3\acZ\c6\d1F&=\00\00\00\00\00`\ec?\00\00\00\00\00\00\00\00\00 \n\839\c7\be?\e0E\e6\afh\c0-\bd\00\00\00\00\00@\ec?\00\00\00\00\00\00\00\00\00\e0\db9\91\e8\bf?\fd\n\a1O\d64%\bd\00\00\00\00\00\00\ec?\00\00\00\00\00\00\00\00\00\e0\'\82\8e\17\c1?\f2\07-\cex\ef!=\00\00\00\00\00\e0\eb?\00\00\00\00\00\00\00\00\00\f0#~+\aa\c1?4\998D\8e\a7,=\00\00\00\00\00\a0\eb?\00\00\00\00\00\00\00\00\00\80\86\0ca\d1\c2?\a1\b4\81\cbl\9d\03=\00\00\00\00\00\80\eb?\00\00\00\00\00\00\00\00\00\90\15\b0\fce\c3?\89rK#\a8/\c6<\00\00\00\00\00@\eb?\00\00\00\00\00\00\00\00\00\b03\83=\91\c4?x\b6\fdTy\83%=\00\00\00\00\00 \eb?\00\00\00\00\00\00\00\00\00\b0\a1\e4\e5\'\c5?\c7}i\e5\e83&=\00\00\00\00\00\e0\ea?\00\00\00\00\00\00\00\00\00\10\8c\beNW\c6?x.<,\8b\cf\19=\00\00\00\00\00\c0\ea?\00\00\00\00\00\00\00\00\00pu\8b\12\f0\c6?\e1!\9c\e5\8d\11%\bd\00\00\00\00\00\a0\ea?\00\00\00\00\00\00\00\00\00PD\85\8d\89\c7?\05C\91p\10f\1c\bd\00\00\00\00\00`\ea?\00\00\00\00\00\00\00\00\00\009\eb\af\be\c8?\d1,\e9\aaT=\07\bd\00\00\00\00\00@\ea?\00\00\00\00\00\00\00\00\00\00\f7\dcZZ\c9?o\ff\a0X(\f2\07=\00\00\00\00\00\00\ea?\00\00\00\00\00\00\00\00\00\e0\8a<\ed\93\ca?i!VPCr(\bd\00\00\00\00\00\e0\e9?\00\00\00\00\00\00\00\00\00\d0[W\d81\cb?\aa\e1\acN\8d5\0c\bd\00\00\00\00\00\c0\e9?\00\00\00\00\00\00\00\00\00\e0;8\87\d0\cb?\b6\12TY\c4K-\bd\00\00\00\00\00\a0\e9?\00\00\00\00\00\00\00\00\00\10\f0\c6\fbo\cc?\d2+\96\c5r\ec\f1\bc\00\00\00\00\00`\e9?\00\00\00\00\00\00\00\00\00\90\d4\b0=\b1\cd?5\b0\15\f7*\ff*\bd\00\00\00\00\00@\e9?\00\00\00\00\00\00\00\00\00\10\e7\ff\0eS\ce?0\f4A`\'\12\c2<\00\00\00\00\00 \e9?\00\00\00\00\00\00\00\00\00\00\dd\e4\ad\f5\ce?\11\8e\bbe\15!\ca\bc\00\00\00\00\00\00\e9?\00\00\00\00\00\00\00\00\00\b0\b3l\1c\99\cf?0\df\0c\ca\ec\cb\1b=\00\00\00\00\00\c0\e8?\00\00\00\00\00\00\00\00\00XM`8q\d0?\91N\ed\16\db\9c\f8<\00\00\00\00\00\a0\e8?\00\00\00\00\00\00\00\00\00`ag-\c4\d0?\e9\ea<\16\8b\18\'=\00\00\00\00\00\80\e8?\00\00\00\00\00\00\00\00\00\e8\'\82\8e\17\d1?\1c\f0\a5c\0e!,\bd\00\00\00\00\00`\e8?\00\00\00\00\00\00\00\00\00\f8\ac\cb\\k\d1?\81\16\a5\f7\cd\9a+=\00\00\00\00\00@\e8?\00\00\00\00\00\00\00\00\00hZc\99\bf\d1?\b7\bdGQ\ed\a6,=\00\00\00\00\00 \e8?\00\00\00\00\00\00\00\00\00\b8\0emE\14\d2?\ea\baF\ba\de\87\n=\00\00\00\00\00\e0\e7?\00\00\00\00\00\00\00\00\00\90\dc|\f0\be\d2?\f4\04PJ\fa\9c*=\00\00\00\00\00\c0\e7?\00\00\00\00\00\00\00\00\00`\d3\e1\f1\14\d3?\b8\9a\ec\ef?\d1f\87\10z^\90\bc\85\7fn\e8\15\e3\ef?\13\f6g5R\d2\8c\be\ef?m{\83]\a6\9a\97<\0f\89\f9lX\b5\ef?\fc\ef\fd\92\1a\b5\8e<\f7Gr+\92\ac\ef?\d1\9c/p=\be><\a2\d1\d32\ec\a3\ef?\0bn\90\894\03j\bc\1b\d3\fe\aff\9b\ef?\0e\bd/*RV\95\bcQ[\12\d0\01\93\ef?U\eaN\8c\ef\80P\bc\cc1l\c0\bd\8a\ef?\16\f4\d5\b9#\c9\91\bc\e0-\a9\ae\9a\82\ef?\afU\\\e9\e3\d3\80\f7\ec\9a<\aa\b9h1\87T\ef?\9d8\86\cb\82\e7\8f\bc\1d\d9\fc\"PM\ef?\8d\c3\a6DAo\8a<\d6\8cb\88;F\ef?}\04\e4\b0\05z\80<\96\dc}\91I?\ef?\94\a8\a8\e3\fd\8e\96<8bunz8\ef?}Ht\f2\18^\87\a9\af\0c\ef?\b6\ab\b0MuM\83<\15\b71\n\fe\06\ef?Lt\ac\e2\01B\86<1\d8L\fcp\01\ef?J\f8\d3]9\dd\8f<\ff\16d\b2\08\fc\ee?\04[\8e;\80\a3\86\bc\f1\9f\92_\c5\f6\ee?hPK\cc\edJ\92\bc\cb\a9:7\a7\f1\ee?\8e-Q\1b\f8\07\99\bcf\d8\05m\ae\ec\ee?\d26\94>\e8\d1q\bc\f7\9f\e54\db\e7\ee?\15\1b\ce\b3\19\19\99\bc\e5\a8\13\c3-\e3\ee?mL*\a7H\9f\85<\"4\12L\a6\de\ee?\8ai(z`\12\93\bc\1c\80\ac\04E\da\ee?[\89\17H\8f\a7X\bc*.\f7!\n\d6\ee?\1b\9aIg\9b,|\bc\97\a8P\d9\f5\d1\ee?\11\ac\c2`\edcC<-\89a`\08\ce\ee?\efd\06;\tf\96Z~d\1fx\bct_\ec\e8u\9f\ee?\b0}\8b\c0J\ee\86\bct\81\a5H\9a\9f\ee?\8a\e6U\1e2\19\86\bc\c9gBV\eb\9f\ee?\d3\d4\t^\cb\9c\90T\'\a4\ee?47;\f1\b6i\93\bc\13\ceL\99\89\a5\ee?\1e\ff\19:\84^\80\bc\ad\c7#F\1a\a7\ee?nWr\d8P\d4\94\bc\ed\92D\9b\d9\a8\ee?\00\8a\0e[g\ad\90<\99f\8a\d9\c7\aa\ee?\b4\ea\f0\c1/\b7\8d<\db\a0*B\e5\ac\ee?\ff\e7\c5\9c`\b6e\bc\8cD\b5\162\af\ee?D_\f3Y\83\f6{<6w\15\99\ae\b1\ee?\83=\1e\a7\1f\t\93\bc\c6\ff\91\0b[\b4\ee?)\1el\8b\b8\a9]\bc\e5\c5\cd\b07\b7\ee?Y\b9\90|\f9#l\bc\0fR\c8\cbD\ba\ee?\aa\f9\f4\"CC\92\bcPN\de\9f\82\bd\ee?K\8ef\d7l\ca\85\bc\ba\07\cap\f1\c0\ee?\'\ce\91+\fc\afq<\90\f0\a3\82\91\c4\ee?\bbs\n\e15\d2m<##\e3\19c\c8\ee?c\"b\"\04\c5\87\bce\e5]{f\cc\ee?\d51\e2\e3\86\1c\8b<3-J\ec\9b\d0\ee?\15\bb\bc\d3\d1\bb\91\bc]%>\b2\03\d5\ee?\d21\ee\9c1\cc\90\b4\07!\d5\82\bc_\9b{3\97|\ef?\c9\0dG;\b9*\89\bc)\a1\f5\14F\86\ef?\d3\88:`\04\b6t<\f6?\8b\e7.\90\ef?qr\9dQ\ec\c5\83<\83L\c7\fbQ\9a\ef?\f0\91\d3\8f\12\f7\8f\bc\da\90\a4\a2\af\a4\ef?}t#\e2\98\ae\8d\bc\f1g\8e-H\af\ef?\08 \aaA\bc\c3\8e<\'Za\ee\1b\ba\ef?2\eb\a9\c3\94+\84<\97\bak7+\c5\ef?\ee\85\d11\a9d\8a<@En[v\d0\ef?\ed\e3;\e4\ba7\8e\bc\14\be\9c\ad\fd\db\ef?\9d\cd\91M;\89w<\d8\90\9e\81\c1\e7\ef?\89\cc`A\c1\05S<\f1q\8f+\c2\f3\ef?") + (data (i32.const 7232) "\07\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\"\1a\00\00\00\00\00\00!\1a\00\00\02\00\00\00a\00\00\00\02\00\00\00\"\t\00\00\00\00\00\00") + (table $0 1 funcref) + (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) + (global $~lib/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0)) + (global $~lib/ASC_SHRINK_LEVEL i32 (i32.const 0)) + (global $assembly/index/DAT_LEN i32 (i32.const 128)) + (global $assembly/index/HLF_LEN i32 (i32.const 64)) + (global $assembly/index/QRT_LEN i32 (i32.const 32)) + (global $assembly/index/pinkNoise i32 (i32.const 768)) + (global $assembly/index/audioData (mut i32) (i32.const 0)) + (global $assembly/index/audioProps (mut i32) (i32.const 0)) + (global $assembly/index/Props.bass i32 (i32.const 0)) + (global $assembly/index/Props.mids i32 (i32.const 1)) + (global $assembly/index/Props.highs i32 (i32.const 2)) + (global $assembly/index/Props.sum i32 (i32.const 3)) + (global $assembly/index/Props.min i32 (i32.const 4)) + (global $assembly/index/Props.max i32 (i32.const 5)) + (global $assembly/index/Props.average i32 (i32.const 6)) + (global $assembly/index/Props.range i32 (i32.const 7)) + (global $assembly/index/Props.silent i32 (i32.const 8)) + (global $assembly/index/Props.intensity i32 (i32.const 9)) + (global $assembly/index/audioSettings (mut i32) (i32.const 0)) + (global $assembly/index/Sett.equalize i32 (i32.const 0)) + (global $assembly/index/Sett.mono_audio i32 (i32.const 1)) + (global $assembly/index/Sett.audio_direction i32 (i32.const 2)) + (global $assembly/index/Sett.peak_filter i32 (i32.const 3)) + (global $assembly/index/Sett.value_smoothing i32 (i32.const 4)) + (global $assembly/index/Sett.audio_increase i32 (i32.const 5)) + (global $assembly/index/Sett.audio_decrease i32 (i32.const 6)) + (global $assembly/index/Sett.minimum_volume i32 (i32.const 7)) + (global $~lib/util/math/log_tail (mut f64) (f64.const 0)) + (global $~lib/rt/__rtti_base i32 (i32.const 7232)) + (global $~lib/memory/__heap_base i32 (i32.const 7292)) + (export "memory" (memory $0)) + (export "__new" (func $~lib/rt/pure/__new)) + (export "__renew" (func $~lib/rt/pure/__renew)) + (export "__retain" (func $~lib/rt/pure/__retain)) + (export "__release" (func $~lib/rt/pure/__release)) + (export "__collect" (func $~lib/rt/pure/__collect)) + (export "__rtti_base" (global $~lib/rt/__rtti_base)) + (export "audioData" (global $assembly/index/audioData)) + (export "audioProps" (global $assembly/index/audioProps)) + (export "audioSettings" (global $assembly/index/audioSettings)) + (export "update" (func $assembly/index/update)) + (start $~start) + (func $~lib/rt/tlsf/removeBlock (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + (local $10 i32) + (local $11 i32) + local.get $1 + i32.load + local.set $2 + i32.const 1 + drop + local.get $2 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 272 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $2 + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.set $3 + i32.const 1 + drop + local.get $3 + i32.const 12 + i32.ge_u + if (result i32) + local.get $3 + i32.const 1073741820 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 274 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $4 + local.get $3 + i32.const 4 + i32.shr_u + local.set $5 + else + i32.const 31 + local.get $3 + i32.clz + i32.sub + local.set $4 + local.get $3 + local.get $4 + i32.const 4 + i32.sub + i32.shr_u + i32.const 1 + i32.const 4 + i32.shl + i32.xor + local.set $5 + local.get $4 + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $4 + end + i32.const 1 + drop + local.get $4 + i32.const 23 + i32.lt_u + if (result i32) + local.get $5 + i32.const 16 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 287 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.load offset=4 + local.set $6 + local.get $1 + i32.load offset=8 + local.set $7 + local.get $6 + if + local.get $6 + local.get $7 + i32.store offset=8 + end + local.get $7 + if + local.get $7 + local.get $6 + i32.store offset=4 + end + local.get $1 + local.get $0 + local.set $10 + local.get $4 + local.set $9 + local.get $5 + local.set $8 + local.get $10 + local.get $9 + i32.const 4 + i32.shl + local.get $8 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + i32.eq + if + local.get $0 + local.set $11 + local.get $4 + local.set $10 + local.get $5 + local.set $9 + local.get $7 + local.set $8 + local.get $11 + local.get $10 + i32.const 4 + i32.shl + local.get $9 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $8 + i32.store offset=96 + local.get $7 + i32.eqz + if + local.get $0 + local.set $9 + local.get $4 + local.set $8 + local.get $9 + local.get $8 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + local.set $9 + local.get $0 + local.set $8 + local.get $4 + local.set $11 + local.get $9 + i32.const 1 + local.get $5 + i32.shl + i32.const -1 + i32.xor + i32.and + local.tee $9 + local.set $10 + local.get $8 + local.get $11 + i32.const 2 + i32.shl + i32.add + local.get $10 + i32.store offset=4 + local.get $9 + i32.eqz + if + local.get $0 + local.get $0 + i32.load + i32.const 1 + local.get $4 + i32.shl + i32.const -1 + i32.xor + i32.and + i32.store + end + end + end + ) + (func $~lib/rt/tlsf/insertBlock (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + (local $10 i32) + (local $11 i32) + (local $12 i32) + (local $13 i32) + i32.const 1 + drop + local.get $1 + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 200 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.load + local.set $2 + i32.const 1 + drop + local.get $2 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 202 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + local.set $3 + local.get $3 + i32.const 4 + i32.add + local.get $3 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.set $4 + local.get $4 + i32.load + local.set $5 + local.get $5 + i32.const 1 + i32.and + if + local.get $2 + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.const 4 + i32.add + local.get $5 + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.set $3 + local.get $3 + i32.const 1073741820 + i32.lt_u + if + local.get $0 + local.get $4 + call $~lib/rt/tlsf/removeBlock + local.get $1 + local.get $2 + i32.const 3 + i32.and + local.get $3 + i32.or + local.tee $2 + i32.store + local.get $1 + local.set $6 + local.get $6 + i32.const 4 + i32.add + local.get $6 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.set $4 + local.get $4 + i32.load + local.set $5 + end + end + local.get $2 + i32.const 2 + i32.and + if + local.get $1 + local.set $6 + local.get $6 + i32.const 4 + i32.sub + i32.load + local.set $6 + local.get $6 + i32.load + local.set $3 + i32.const 1 + drop + local.get $3 + i32.const 1 + i32.and + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 223 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.const 4 + i32.add + local.get $2 + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.set $7 + local.get $7 + i32.const 1073741820 + i32.lt_u + if + local.get $0 + local.get $6 + call $~lib/rt/tlsf/removeBlock + local.get $6 + local.get $3 + i32.const 3 + i32.and + local.get $7 + i32.or + local.tee $2 + i32.store + local.get $6 + local.set $1 + end + end + local.get $4 + local.get $5 + i32.const 2 + i32.or + i32.store + local.get $2 + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.set $8 + i32.const 1 + drop + local.get $8 + i32.const 12 + i32.ge_u + if (result i32) + local.get $8 + i32.const 1073741820 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 238 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + i32.const 1 + drop + local.get $1 + i32.const 4 + i32.add + local.get $8 + i32.add + local.get $4 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 239 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $4 + i32.const 4 + i32.sub + local.get $1 + i32.store + local.get $8 + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $9 + local.get $8 + i32.const 4 + i32.shr_u + local.set $10 + else + i32.const 31 + local.get $8 + i32.clz + i32.sub + local.set $9 + local.get $8 + local.get $9 + i32.const 4 + i32.sub + i32.shr_u + i32.const 1 + i32.const 4 + i32.shl + i32.xor + local.set $10 + local.get $9 + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $9 + end + i32.const 1 + drop + local.get $9 + i32.const 23 + i32.lt_u + if (result i32) + local.get $10 + i32.const 16 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 255 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.set $7 + local.get $9 + local.set $3 + local.get $10 + local.set $6 + local.get $7 + local.get $3 + i32.const 4 + i32.shl + local.get $6 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + local.set $11 + local.get $1 + i32.const 0 + i32.store offset=4 + local.get $1 + local.get $11 + i32.store offset=8 + local.get $11 + if + local.get $11 + local.get $1 + i32.store offset=4 + end + local.get $0 + local.set $12 + local.get $9 + local.set $7 + local.get $10 + local.set $3 + local.get $1 + local.set $6 + local.get $12 + local.get $7 + i32.const 4 + i32.shl + local.get $3 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $6 + i32.store offset=96 + local.get $0 + local.get $0 + i32.load + i32.const 1 + local.get $9 + i32.shl + i32.or + i32.store + local.get $0 + local.set $13 + local.get $9 + local.set $12 + local.get $0 + local.set $3 + local.get $9 + local.set $6 + local.get $3 + local.get $6 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + i32.const 1 + local.get $10 + i32.shl + i32.or + local.set $7 + local.get $13 + local.get $12 + i32.const 2 + i32.shl + i32.add + local.get $7 + i32.store offset=4 + ) + (func $~lib/rt/tlsf/addMemory (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + i32.const 1 + drop + local.get $1 + local.get $2 + i32.le_u + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 380 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 4 + i32.add + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + i32.const 4 + i32.sub + local.set $1 + local.get $2 + i32.const 15 + i32.const -1 + i32.xor + i32.and + local.set $2 + local.get $0 + local.set $3 + local.get $3 + i32.load offset=1568 + local.set $4 + i32.const 0 + local.set $5 + local.get $4 + if + i32.const 1 + drop + local.get $1 + local.get $4 + i32.const 4 + i32.add + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 387 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $1 + i32.const 16 + i32.sub + local.get $4 + i32.eq + if + local.get $1 + i32.const 16 + i32.sub + local.set $1 + local.get $4 + i32.load + local.set $5 + else + nop + end + else + i32.const 1 + drop + local.get $1 + local.get $0 + i32.const 1572 + i32.add + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 400 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + end + local.get $2 + local.get $1 + i32.sub + local.set $6 + local.get $6 + i32.const 4 + i32.const 12 + i32.add + i32.const 4 + i32.add + i32.lt_u + if + i32.const 0 + return + end + local.get $6 + i32.const 2 + i32.const 4 + i32.mul + i32.sub + local.set $7 + local.get $1 + local.set $8 + local.get $8 + local.get $7 + i32.const 1 + i32.or + local.get $5 + i32.const 2 + i32.and + i32.or + i32.store + local.get $8 + i32.const 0 + i32.store offset=4 + local.get $8 + i32.const 0 + i32.store offset=8 + local.get $1 + i32.const 4 + i32.add + local.get $7 + i32.add + local.set $4 + local.get $4 + i32.const 0 + i32.const 2 + i32.or + i32.store + local.get $0 + local.set $9 + local.get $4 + local.set $3 + local.get $9 + local.get $3 + i32.store offset=1568 + local.get $0 + local.get $8 + call $~lib/rt/tlsf/insertBlock + i32.const 1 + ) + (func $~lib/rt/tlsf/initialize + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + (local $10 i32) + (local $11 i32) + (local $12 i32) + global.get $~lib/memory/__heap_base + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + local.set $0 + memory.size + local.set $1 + local.get $0 + i32.const 1572 + i32.add + i32.const 65535 + i32.add + i32.const 65535 + i32.const -1 + i32.xor + i32.and + i32.const 16 + i32.shr_u + local.set $2 + local.get $2 + local.get $1 + i32.gt_s + if (result i32) + local.get $2 + local.get $1 + i32.sub + memory.grow + i32.const 0 + i32.lt_s + else + i32.const 0 + end + if + unreachable + end + local.get $0 + local.set $3 + local.get $3 + i32.const 0 + i32.store + local.get $3 + local.set $5 + i32.const 0 + local.set $4 + local.get $5 + local.get $4 + i32.store offset=1568 + i32.const 0 + local.set $5 + loop $for-loop|0 + local.get $5 + i32.const 23 + i32.lt_u + local.set $4 + local.get $4 + if + local.get $3 + local.set $8 + local.get $5 + local.set $7 + i32.const 0 + local.set $6 + local.get $8 + local.get $7 + i32.const 2 + i32.shl + i32.add + local.get $6 + i32.store offset=4 + i32.const 0 + local.set $8 + loop $for-loop|1 + local.get $8 + i32.const 16 + i32.lt_u + local.set $7 + local.get $7 + if + local.get $3 + local.set $11 + local.get $5 + local.set $10 + local.get $8 + local.set $9 + i32.const 0 + local.set $6 + local.get $11 + local.get $10 + i32.const 4 + i32.shl + local.get $9 + i32.add + i32.const 2 + i32.shl + i32.add + local.get $6 + i32.store offset=96 + local.get $8 + i32.const 1 + i32.add + local.set $8 + br $for-loop|1 + end + end + local.get $5 + i32.const 1 + i32.add + local.set $5 + br $for-loop|0 + end + end + local.get $0 + i32.const 1572 + i32.add + local.set $12 + i32.const 0 + drop + local.get $3 + local.get $12 + memory.size + i32.const 16 + i32.shl + call $~lib/rt/tlsf/addMemory + drop + local.get $3 + global.set $~lib/rt/tlsf/ROOT + ) + (func $~lib/rt/tlsf/computeSize (param $0 i32) (result i32) + local.get $0 + i32.const 12 + i32.le_u + if (result i32) + i32.const 12 + else + local.get $0 + i32.const 4 + i32.add + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + i32.const 4 + i32.sub + end + ) + (func $~lib/rt/tlsf/prepareSize (param $0 i32) (result i32) + local.get $0 + i32.const 1073741820 + i32.ge_u + if + i32.const 32 + i32.const 160 + i32.const 461 + i32.const 30 + call $~lib/builtins/abort + unreachable + end + local.get $0 + call $~lib/rt/tlsf/computeSize + ) + (func $~lib/rt/tlsf/searchBlock (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i32) + local.get $1 + i32.const 256 + i32.lt_u + if + i32.const 0 + local.set $2 + local.get $1 + i32.const 4 + i32.shr_u + local.set $3 + else + local.get $1 + i32.const 536870910 + i32.lt_u + if (result i32) + local.get $1 + i32.const 1 + i32.const 27 + local.get $1 + i32.clz + i32.sub + i32.shl + i32.add + i32.const 1 + i32.sub + else + local.get $1 + end + local.set $4 + i32.const 31 + local.get $4 + i32.clz + i32.sub + local.set $2 + local.get $4 + local.get $2 + i32.const 4 + i32.sub + i32.shr_u + i32.const 1 + i32.const 4 + i32.shl + i32.xor + local.set $3 + local.get $2 + i32.const 8 + i32.const 1 + i32.sub + i32.sub + local.set $2 + end + i32.const 1 + drop + local.get $2 + i32.const 23 + i32.lt_u + if (result i32) + local.get $3 + i32.const 16 + i32.lt_u + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 333 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.set $5 + local.get $2 + local.set $4 + local.get $5 + local.get $4 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + i32.const 0 + i32.const -1 + i32.xor + local.get $3 + i32.shl + i32.and + local.set $6 + i32.const 0 + local.set $7 + local.get $6 + i32.eqz + if + local.get $0 + i32.load + i32.const 0 + i32.const -1 + i32.xor + local.get $2 + i32.const 1 + i32.add + i32.shl + i32.and + local.set $5 + local.get $5 + i32.eqz + if + i32.const 0 + local.set $7 + else + local.get $5 + i32.ctz + local.set $2 + local.get $0 + local.set $8 + local.get $2 + local.set $4 + local.get $8 + local.get $4 + i32.const 2 + i32.shl + i32.add + i32.load offset=4 + local.set $6 + i32.const 1 + drop + local.get $6 + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 346 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.set $9 + local.get $2 + local.set $8 + local.get $6 + i32.ctz + local.set $4 + local.get $9 + local.get $8 + i32.const 4 + i32.shl + local.get $4 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + local.set $7 + end + else + local.get $0 + local.set $9 + local.get $2 + local.set $8 + local.get $6 + i32.ctz + local.set $4 + local.get $9 + local.get $8 + i32.const 4 + i32.shl + local.get $4 + i32.add + i32.const 2 + i32.shl + i32.add + i32.load offset=96 + local.set $7 + end + local.get $7 + ) + (func $~lib/rt/tlsf/growMemory (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + i32.const 0 + drop + local.get $1 + i32.const 536870910 + i32.lt_u + if + local.get $1 + i32.const 1 + i32.const 27 + local.get $1 + i32.clz + i32.sub + i32.shl + i32.const 1 + i32.sub + i32.add + local.set $1 + end + memory.size + local.set $2 + local.get $1 + i32.const 4 + local.get $2 + i32.const 16 + i32.shl + i32.const 4 + i32.sub + local.get $0 + local.set $3 + local.get $3 + i32.load offset=1568 + i32.ne + i32.shl + i32.add + local.set $1 + local.get $1 + i32.const 65535 + i32.add + i32.const 65535 + i32.const -1 + i32.xor + i32.and + i32.const 16 + i32.shr_u + local.set $4 + local.get $2 + local.tee $3 + local.get $4 + local.tee $5 + local.get $3 + local.get $5 + i32.gt_s + select + local.set $6 + local.get $6 + memory.grow + i32.const 0 + i32.lt_s + if + local.get $4 + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + memory.size + local.set $7 + local.get $0 + local.get $2 + i32.const 16 + i32.shl + local.get $7 + i32.const 16 + i32.shl + call $~lib/rt/tlsf/addMemory + drop + ) + (func $~lib/rt/tlsf/prepareBlock (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $1 + i32.load + local.set $3 + i32.const 1 + drop + local.get $2 + i32.const 4 + i32.add + i32.const 15 + i32.and + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 360 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.get $2 + i32.sub + local.set $4 + local.get $4 + i32.const 4 + i32.const 12 + i32.add + i32.ge_u + if + local.get $1 + local.get $2 + local.get $3 + i32.const 2 + i32.and + i32.or + i32.store + local.get $1 + i32.const 4 + i32.add + local.get $2 + i32.add + local.set $5 + local.get $5 + local.get $4 + i32.const 4 + i32.sub + i32.const 1 + i32.or + i32.store + local.get $0 + local.get $5 + call $~lib/rt/tlsf/insertBlock + else + local.get $1 + local.get $3 + i32.const 1 + i32.const -1 + i32.xor + i32.and + i32.store + local.get $1 + local.set $5 + local.get $5 + i32.const 4 + i32.add + local.get $5 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.get $1 + local.set $5 + local.get $5 + i32.const 4 + i32.add + local.get $5 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + i32.load + i32.const 2 + i32.const -1 + i32.xor + i32.and + i32.store + end + ) + (func $~lib/rt/tlsf/allocateBlock (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + local.get $1 + call $~lib/rt/tlsf/prepareSize + local.set $2 + local.get $0 + local.get $2 + call $~lib/rt/tlsf/searchBlock + local.set $3 + local.get $3 + i32.eqz + if + local.get $0 + local.get $2 + call $~lib/rt/tlsf/growMemory + local.get $0 + local.get $2 + call $~lib/rt/tlsf/searchBlock + local.set $3 + i32.const 1 + drop + local.get $3 + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 498 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + end + i32.const 1 + drop + local.get $3 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.get $2 + i32.ge_u + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 500 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $3 + call $~lib/rt/tlsf/removeBlock + local.get $0 + local.get $3 + local.get $2 + call $~lib/rt/tlsf/prepareBlock + i32.const 0 + drop + local.get $3 + ) + (func $~lib/rt/tlsf/__alloc (param $0 i32) (result i32) + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + global.get $~lib/rt/tlsf/ROOT + local.get $0 + call $~lib/rt/tlsf/allocateBlock + i32.const 4 + i32.add + ) + (func $~lib/rt/pure/__new (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + local.get $0 + i32.const 1073741804 + i32.gt_u + if + i32.const 32 + i32.const 96 + i32.const 275 + i32.const 30 + call $~lib/builtins/abort + unreachable + end + i32.const 16 + local.get $0 + i32.add + call $~lib/rt/tlsf/__alloc + local.set $2 + local.get $2 + i32.const 4 + i32.sub + local.set $3 + local.get $3 + i32.const 0 + i32.store offset=4 + local.get $3 + i32.const 0 + i32.store offset=8 + local.get $3 + local.get $1 + i32.store offset=12 + local.get $3 + local.get $0 + i32.store offset=16 + local.get $2 + i32.const 16 + i32.add + ) + (func $~lib/rt/tlsf/checkUsedBlock (param $0 i32) (result i32) + (local $1 i32) + local.get $0 + i32.const 4 + i32.sub + local.set $1 + local.get $0 + i32.const 0 + i32.ne + if (result i32) + local.get $0 + i32.const 15 + i32.and + i32.eqz + else + i32.const 0 + end + if (result i32) + local.get $1 + i32.load + i32.const 1 + i32.and + i32.eqz + else + i32.const 0 + end + i32.eqz + if + i32.const 0 + i32.const 160 + i32.const 563 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $1 + ) + (func $~lib/util/memory/memcpy (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + loop $while-continue|0 + local.get $2 + if (result i32) + local.get $1 + i32.const 3 + i32.and + else + i32.const 0 + end + local.set $5 + local.get $5 + if + local.get $0 + local.tee $6 + i32.const 1 + i32.add + local.set $0 + local.get $6 + local.get $1 + local.tee $6 + i32.const 1 + i32.add + local.set $1 + local.get $6 + i32.load8_u + i32.store8 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + br $while-continue|0 + end + end + local.get $0 + i32.const 3 + i32.and + i32.const 0 + i32.eq + if + loop $while-continue|1 + local.get $2 + i32.const 16 + i32.ge_u + local.set $5 + local.get $5 + if + local.get $0 + local.get $1 + i32.load + i32.store + local.get $0 + i32.const 4 + i32.add + local.get $1 + i32.const 4 + i32.add + i32.load + i32.store + local.get $0 + i32.const 8 + i32.add + local.get $1 + i32.const 8 + i32.add + i32.load + i32.store + local.get $0 + i32.const 12 + i32.add + local.get $1 + i32.const 12 + i32.add + i32.load + i32.store + local.get $1 + i32.const 16 + i32.add + local.set $1 + local.get $0 + i32.const 16 + i32.add + local.set $0 + local.get $2 + i32.const 16 + i32.sub + local.set $2 + br $while-continue|1 + end + end + local.get $2 + i32.const 8 + i32.and + if + local.get $0 + local.get $1 + i32.load + i32.store + local.get $0 + i32.const 4 + i32.add + local.get $1 + i32.const 4 + i32.add + i32.load + i32.store + local.get $0 + i32.const 8 + i32.add + local.set $0 + local.get $1 + i32.const 8 + i32.add + local.set $1 + end + local.get $2 + i32.const 4 + i32.and + if + local.get $0 + local.get $1 + i32.load + i32.store + local.get $0 + i32.const 4 + i32.add + local.set $0 + local.get $1 + i32.const 4 + i32.add + local.set $1 + end + local.get $2 + i32.const 2 + i32.and + if + local.get $0 + local.get $1 + i32.load16_u + i32.store16 + local.get $0 + i32.const 2 + i32.add + local.set $0 + local.get $1 + i32.const 2 + i32.add + local.set $1 + end + local.get $2 + i32.const 1 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + return + end + local.get $2 + i32.const 32 + i32.ge_u + if + block $break|2 + block $case2|2 + block $case1|2 + block $case0|2 + local.get $0 + i32.const 3 + i32.and + local.set $5 + local.get $5 + i32.const 1 + i32.eq + br_if $case0|2 + local.get $5 + i32.const 2 + i32.eq + br_if $case1|2 + local.get $5 + i32.const 3 + i32.eq + br_if $case2|2 + br $break|2 + end + local.get $1 + i32.load + local.set $3 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $2 + i32.const 3 + i32.sub + local.set $2 + loop $while-continue|3 + local.get $2 + i32.const 17 + i32.ge_u + local.set $5 + local.get $5 + if + local.get $1 + i32.const 1 + i32.add + i32.load + local.set $4 + local.get $0 + local.get $3 + i32.const 24 + i32.shr_u + local.get $4 + i32.const 8 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 5 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 4 + i32.add + local.get $4 + i32.const 24 + i32.shr_u + local.get $3 + i32.const 8 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 9 + i32.add + i32.load + local.set $4 + local.get $0 + i32.const 8 + i32.add + local.get $3 + i32.const 24 + i32.shr_u + local.get $4 + i32.const 8 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 13 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 12 + i32.add + local.get $4 + i32.const 24 + i32.shr_u + local.get $3 + i32.const 8 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 16 + i32.add + local.set $1 + local.get $0 + i32.const 16 + i32.add + local.set $0 + local.get $2 + i32.const 16 + i32.sub + local.set $2 + br $while-continue|3 + end + end + br $break|2 + end + local.get $1 + i32.load + local.set $3 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $2 + i32.const 2 + i32.sub + local.set $2 + loop $while-continue|4 + local.get $2 + i32.const 18 + i32.ge_u + local.set $5 + local.get $5 + if + local.get $1 + i32.const 2 + i32.add + i32.load + local.set $4 + local.get $0 + local.get $3 + i32.const 16 + i32.shr_u + local.get $4 + i32.const 16 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 6 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 4 + i32.add + local.get $4 + i32.const 16 + i32.shr_u + local.get $3 + i32.const 16 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 10 + i32.add + i32.load + local.set $4 + local.get $0 + i32.const 8 + i32.add + local.get $3 + i32.const 16 + i32.shr_u + local.get $4 + i32.const 16 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 14 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 12 + i32.add + local.get $4 + i32.const 16 + i32.shr_u + local.get $3 + i32.const 16 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 16 + i32.add + local.set $1 + local.get $0 + i32.const 16 + i32.add + local.set $0 + local.get $2 + i32.const 16 + i32.sub + local.set $2 + br $while-continue|4 + end + end + br $break|2 + end + local.get $1 + i32.load + local.set $3 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $2 + i32.const 1 + i32.sub + local.set $2 + loop $while-continue|5 + local.get $2 + i32.const 19 + i32.ge_u + local.set $5 + local.get $5 + if + local.get $1 + i32.const 3 + i32.add + i32.load + local.set $4 + local.get $0 + local.get $3 + i32.const 8 + i32.shr_u + local.get $4 + i32.const 24 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 7 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 4 + i32.add + local.get $4 + i32.const 8 + i32.shr_u + local.get $3 + i32.const 24 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 11 + i32.add + i32.load + local.set $4 + local.get $0 + i32.const 8 + i32.add + local.get $3 + i32.const 8 + i32.shr_u + local.get $4 + i32.const 24 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 15 + i32.add + i32.load + local.set $3 + local.get $0 + i32.const 12 + i32.add + local.get $4 + i32.const 8 + i32.shr_u + local.get $3 + i32.const 24 + i32.shl + i32.or + i32.store + local.get $1 + i32.const 16 + i32.add + local.set $1 + local.get $0 + i32.const 16 + i32.add + local.set $0 + local.get $2 + i32.const 16 + i32.sub + local.set $2 + br $while-continue|5 + end + end + br $break|2 + end + end + local.get $2 + i32.const 16 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + local.get $2 + i32.const 8 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + local.get $2 + i32.const 4 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + local.get $2 + i32.const 2 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + local.get $2 + i32.const 1 + i32.and + if + local.get $0 + local.tee $5 + i32.const 1 + i32.add + local.set $0 + local.get $5 + local.get $1 + local.tee $5 + i32.const 1 + i32.add + local.set $1 + local.get $5 + i32.load8_u + i32.store8 + end + ) + (func $~lib/memory/memory.copy (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + block $~lib/util/memory/memmove|inlined.0 + local.get $0 + local.set $5 + local.get $1 + local.set $4 + local.get $2 + local.set $3 + local.get $5 + local.get $4 + i32.eq + if + br $~lib/util/memory/memmove|inlined.0 + end + i32.const 0 + i32.const 1 + i32.lt_s + drop + local.get $4 + local.get $5 + i32.sub + local.get $3 + i32.sub + i32.const 0 + local.get $3 + i32.const 1 + i32.shl + i32.sub + i32.le_u + if + local.get $5 + local.get $4 + local.get $3 + call $~lib/util/memory/memcpy + br $~lib/util/memory/memmove|inlined.0 + end + local.get $5 + local.get $4 + i32.lt_u + if + i32.const 0 + i32.const 2 + i32.lt_s + drop + local.get $4 + i32.const 7 + i32.and + local.get $5 + i32.const 7 + i32.and + i32.eq + if + loop $while-continue|0 + local.get $5 + i32.const 7 + i32.and + local.set $6 + local.get $6 + if + local.get $3 + i32.eqz + if + br $~lib/util/memory/memmove|inlined.0 + end + local.get $3 + i32.const 1 + i32.sub + local.set $3 + local.get $5 + local.tee $7 + i32.const 1 + i32.add + local.set $5 + local.get $7 + local.get $4 + local.tee $7 + i32.const 1 + i32.add + local.set $4 + local.get $7 + i32.load8_u + i32.store8 + br $while-continue|0 + end + end + loop $while-continue|1 + local.get $3 + i32.const 8 + i32.ge_u + local.set $6 + local.get $6 + if + local.get $5 + local.get $4 + i64.load + i64.store + local.get $3 + i32.const 8 + i32.sub + local.set $3 + local.get $5 + i32.const 8 + i32.add + local.set $5 + local.get $4 + i32.const 8 + i32.add + local.set $4 + br $while-continue|1 + end + end + end + loop $while-continue|2 + local.get $3 + local.set $6 + local.get $6 + if + local.get $5 + local.tee $7 + i32.const 1 + i32.add + local.set $5 + local.get $7 + local.get $4 + local.tee $7 + i32.const 1 + i32.add + local.set $4 + local.get $7 + i32.load8_u + i32.store8 + local.get $3 + i32.const 1 + i32.sub + local.set $3 + br $while-continue|2 + end + end + else + i32.const 0 + i32.const 2 + i32.lt_s + drop + local.get $4 + i32.const 7 + i32.and + local.get $5 + i32.const 7 + i32.and + i32.eq + if + loop $while-continue|3 + local.get $5 + local.get $3 + i32.add + i32.const 7 + i32.and + local.set $6 + local.get $6 + if + local.get $3 + i32.eqz + if + br $~lib/util/memory/memmove|inlined.0 + end + local.get $5 + local.get $3 + i32.const 1 + i32.sub + local.tee $3 + i32.add + local.get $4 + local.get $3 + i32.add + i32.load8_u + i32.store8 + br $while-continue|3 + end + end + loop $while-continue|4 + local.get $3 + i32.const 8 + i32.ge_u + local.set $6 + local.get $6 + if + local.get $3 + i32.const 8 + i32.sub + local.set $3 + local.get $5 + local.get $3 + i32.add + local.get $4 + local.get $3 + i32.add + i64.load + i64.store + br $while-continue|4 + end + end + end + loop $while-continue|5 + local.get $3 + local.set $6 + local.get $6 + if + local.get $5 + local.get $3 + i32.const 1 + i32.sub + local.tee $3 + i32.add + local.get $4 + local.get $3 + i32.add + i32.load8_u + i32.store8 + br $while-continue|5 + end + end + end + end + ) + (func $~lib/rt/tlsf/freeBlock (param $0 i32) (param $1 i32) + local.get $1 + local.get $1 + i32.load + i32.const 1 + i32.or + i32.store + i32.const 0 + drop + local.get $0 + local.get $1 + call $~lib/rt/tlsf/insertBlock + ) + (func $~lib/rt/tlsf/moveBlock (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + local.get $0 + local.get $2 + call $~lib/rt/tlsf/allocateBlock + local.set $3 + local.get $3 + i32.const 4 + i32.add + local.get $1 + i32.const 4 + i32.add + local.get $1 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + call $~lib/memory/memory.copy + local.get $1 + global.get $~lib/memory/__heap_base + i32.ge_u + if + i32.const 0 + drop + local.get $0 + local.get $1 + call $~lib/rt/tlsf/freeBlock + end + local.get $3 + ) + (func $~lib/rt/tlsf/reallocateBlock (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + local.get $2 + call $~lib/rt/tlsf/prepareSize + local.set $3 + local.get $1 + i32.load + local.set $4 + local.get $4 + i32.const 3 + i32.const -1 + i32.xor + i32.and + local.set $5 + local.get $3 + local.get $5 + i32.le_u + if + local.get $0 + local.get $1 + local.get $3 + call $~lib/rt/tlsf/prepareBlock + i32.const 0 + drop + local.get $1 + return + end + local.get $1 + local.set $6 + local.get $6 + i32.const 4 + i32.add + local.get $6 + i32.load + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.set $7 + local.get $7 + i32.load + local.set $8 + local.get $8 + i32.const 1 + i32.and + if + local.get $5 + i32.const 4 + i32.add + local.get $8 + i32.const 3 + i32.const -1 + i32.xor + i32.and + i32.add + local.set $6 + local.get $6 + local.get $3 + i32.ge_u + if + local.get $0 + local.get $7 + call $~lib/rt/tlsf/removeBlock + local.get $1 + local.get $4 + i32.const 3 + i32.and + local.get $6 + i32.or + i32.store + local.get $0 + local.get $1 + local.get $3 + call $~lib/rt/tlsf/prepareBlock + i32.const 0 + drop + local.get $1 + return + end + end + local.get $0 + local.get $1 + local.get $2 + call $~lib/rt/tlsf/moveBlock + ) + (func $~lib/rt/tlsf/__realloc (param $0 i32) (param $1 i32) (result i32) + global.get $~lib/rt/tlsf/ROOT + i32.eqz + if + call $~lib/rt/tlsf/initialize + end + local.get $0 + global.get $~lib/memory/__heap_base + i32.lt_u + if (result i32) + global.get $~lib/rt/tlsf/ROOT + local.get $0 + call $~lib/rt/tlsf/checkUsedBlock + local.get $1 + call $~lib/rt/tlsf/moveBlock + else + global.get $~lib/rt/tlsf/ROOT + local.get $0 + call $~lib/rt/tlsf/checkUsedBlock + local.get $1 + call $~lib/rt/tlsf/reallocateBlock + end + i32.const 4 + i32.add + ) + (func $~lib/rt/pure/__renew (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + local.get $1 + i32.const 1073741804 + i32.gt_u + if + i32.const 32 + i32.const 96 + i32.const 288 + i32.const 30 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 16 + i32.sub + i32.const 16 + local.get $1 + i32.add + call $~lib/rt/tlsf/__realloc + local.set $2 + local.get $2 + i32.const 4 + i32.sub + local.get $1 + i32.store offset=16 + local.get $2 + i32.const 16 + i32.add + ) + (func $~lib/rt/pure/increment (param $0 i32) + (local $1 i32) + local.get $0 + i32.load offset=4 + local.set $1 + local.get $1 + i32.const 268435455 + i32.const -1 + i32.xor + i32.and + local.get $1 + i32.const 1 + i32.add + i32.const 268435455 + i32.const -1 + i32.xor + i32.and + i32.eq + i32.eqz + if + i32.const 0 + i32.const 96 + i32.const 109 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + i32.const 1 + i32.add + i32.store offset=4 + i32.const 0 + drop + i32.const 1 + drop + local.get $0 + i32.load + i32.const 1 + i32.and + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 96 + i32.const 112 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + ) + (func $~lib/rt/pure/__retain (param $0 i32) (result i32) + local.get $0 + global.get $~lib/memory/__heap_base + i32.gt_u + if + local.get $0 + i32.const 20 + i32.sub + call $~lib/rt/pure/increment + end + local.get $0 + ) + (func $~lib/rt/pure/__release (param $0 i32) + local.get $0 + global.get $~lib/memory/__heap_base + i32.gt_u + if + local.get $0 + i32.const 20 + i32.sub + call $~lib/rt/pure/decrement + end + ) + (func $~lib/memory/memory.fill (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 i64) + (local $10 i32) + block $~lib/util/memory/memset|inlined.0 + local.get $0 + local.set $5 + local.get $1 + local.set $4 + local.get $2 + local.set $3 + i32.const 0 + i32.const 1 + i32.gt_s + drop + local.get $3 + i32.eqz + if + br $~lib/util/memory/memset|inlined.0 + end + local.get $5 + local.get $3 + i32.add + i32.const 4 + i32.sub + local.set $6 + local.get $5 + local.get $4 + i32.store8 + local.get $6 + local.get $4 + i32.store8 offset=3 + local.get $3 + i32.const 2 + i32.le_u + if + br $~lib/util/memory/memset|inlined.0 + end + local.get $5 + local.get $4 + i32.store8 offset=1 + local.get $5 + local.get $4 + i32.store8 offset=2 + local.get $6 + local.get $4 + i32.store8 offset=2 + local.get $6 + local.get $4 + i32.store8 offset=1 + local.get $3 + i32.const 6 + i32.le_u + if + br $~lib/util/memory/memset|inlined.0 + end + local.get $5 + local.get $4 + i32.store8 offset=3 + local.get $6 + local.get $4 + i32.store8 + local.get $3 + i32.const 8 + i32.le_u + if + br $~lib/util/memory/memset|inlined.0 + end + i32.const 0 + local.get $5 + i32.sub + i32.const 3 + i32.and + local.set $7 + local.get $5 + local.get $7 + i32.add + local.set $5 + local.get $3 + local.get $7 + i32.sub + local.set $3 + local.get $3 + i32.const -4 + i32.and + local.set $3 + i32.const -1 + i32.const 255 + i32.div_u + local.get $4 + i32.const 255 + i32.and + i32.mul + local.set $8 + local.get $5 + local.get $3 + i32.add + i32.const 28 + i32.sub + local.set $6 + local.get $5 + local.get $8 + i32.store + local.get $6 + local.get $8 + i32.store offset=24 + local.get $3 + i32.const 8 + i32.le_u + if + br $~lib/util/memory/memset|inlined.0 + end + local.get $5 + local.get $8 + i32.store offset=4 + local.get $5 + local.get $8 + i32.store offset=8 + local.get $6 + local.get $8 + i32.store offset=16 + local.get $6 + local.get $8 + i32.store offset=20 + local.get $3 + i32.const 24 + i32.le_u + if + br $~lib/util/memory/memset|inlined.0 + end + local.get $5 + local.get $8 + i32.store offset=12 + local.get $5 + local.get $8 + i32.store offset=16 + local.get $5 + local.get $8 + i32.store offset=20 + local.get $5 + local.get $8 + i32.store offset=24 + local.get $6 + local.get $8 + i32.store + local.get $6 + local.get $8 + i32.store offset=4 + local.get $6 + local.get $8 + i32.store offset=8 + local.get $6 + local.get $8 + i32.store offset=12 + i32.const 24 + local.get $5 + i32.const 4 + i32.and + i32.add + local.set $7 + local.get $5 + local.get $7 + i32.add + local.set $5 + local.get $3 + local.get $7 + i32.sub + local.set $3 + local.get $8 + i64.extend_i32_u + local.get $8 + i64.extend_i32_u + i64.const 32 + i64.shl + i64.or + local.set $9 + loop $while-continue|0 + local.get $3 + i32.const 32 + i32.ge_u + local.set $10 + local.get $10 + if + local.get $5 + local.get $9 + i64.store + local.get $5 + local.get $9 + i64.store offset=8 + local.get $5 + local.get $9 + i64.store offset=16 + local.get $5 + local.get $9 + i64.store offset=24 + local.get $3 + i32.const 32 + i32.sub + local.set $3 + local.get $5 + i32.const 32 + i32.add + local.set $5 + br $while-continue|0 + end + end + end + ) + (func $~lib/arraybuffer/ArrayBufferView#constructor (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + local.get $0 + i32.eqz + if + i32.const 12 + i32.const 2 + call $~lib/rt/pure/__new + call $~lib/rt/pure/__retain + local.set $0 + end + local.get $0 + i32.const 0 + i32.store + local.get $0 + i32.const 0 + i32.store offset=4 + local.get $0 + i32.const 0 + i32.store offset=8 + local.get $1 + i32.const 1073741820 + local.get $2 + i32.shr_u + i32.gt_u + if + i32.const 816 + i32.const 864 + i32.const 18 + i32.const 57 + call $~lib/builtins/abort + unreachable + end + local.get $1 + local.get $2 + i32.shl + local.tee $1 + i32.const 0 + call $~lib/rt/pure/__new + local.set $3 + local.get $3 + i32.const 0 + local.get $1 + call $~lib/memory/memory.fill + local.get $0 + local.tee $4 + local.get $3 + local.tee $5 + local.get $4 + i32.load + local.tee $6 + i32.ne + if + local.get $5 + call $~lib/rt/pure/__retain + local.set $5 + local.get $6 + call $~lib/rt/pure/__release + end + local.get $5 + i32.store + local.get $0 + local.get $3 + i32.store offset=4 + local.get $0 + local.get $1 + i32.store offset=8 + local.get $0 + ) + (func $~lib/typedarray/Float64Array#constructor (param $0 i32) (param $1 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 12 + i32.const 4 + call $~lib/rt/pure/__new + call $~lib/rt/pure/__retain + local.set $0 + end + local.get $0 + local.get $1 + i32.const 3 + call $~lib/arraybuffer/ArrayBufferView#constructor + local.set $0 + local.get $0 + ) + (func $start:assembly/index + (local $0 i32) + (local $1 i32) + i32.const 0 + global.get $assembly/index/DAT_LEN + call $~lib/typedarray/Float64Array#constructor + global.set $assembly/index/audioData + i32.const 0 + i32.const 10 + call $~lib/typedarray/Float64Array#constructor + global.set $assembly/index/audioProps + i32.const 0 + i32.const 8 + call $~lib/typedarray/Float64Array#constructor + global.set $assembly/index/audioSettings + ) + (func $~lib/typedarray/Float64Array#__get (param $0 i32) (param $1 i32) (result f64) + local.get $1 + local.get $0 + i32.load offset=8 + i32.const 3 + i32.shr_u + i32.ge_u + if + i32.const 928 + i32.const 992 + i32.const 1304 + i32.const 64 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=4 + local.get $1 + i32.const 3 + i32.shl + i32.add + f64.load + ) + (func $assembly/index/isOn (param $0 f64) (result i32) + local.get $0 + f64.const 0 + f64.gt + ) + (func $~lib/array/Array#__uget (param $0 i32) (param $1 i32) (result f64) + local.get $0 + i32.load offset=4 + local.get $1 + i32.const 3 + i32.shl + i32.add + f64.load + ) + (func $~lib/array/Array#__get (param $0 i32) (param $1 i32) (result f64) + (local $2 f64) + local.get $1 + local.get $0 + i32.load offset=12 + i32.ge_u + if + i32.const 928 + i32.const 1056 + i32.const 104 + i32.const 42 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.get $1 + call $~lib/array/Array#__uget + local.set $2 + i32.const 0 + drop + local.get $2 + ) + (func $~lib/typedarray/Float64Array#__set (param $0 i32) (param $1 i32) (param $2 f64) + local.get $1 + local.get $0 + i32.load offset=8 + i32.const 3 + i32.shr_u + i32.ge_u + if + i32.const 928 + i32.const 992 + i32.const 1315 + i32.const 64 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=4 + local.get $1 + i32.const 3 + i32.shl + i32.add + local.get $2 + f64.store + ) + (func $~lib/typedarray/Float64Array#get:length (param $0 i32) (result i32) + local.get $0 + i32.load offset=8 + i32.const 3 + i32.shr_u + ) + (func $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $1 + call $~lib/rt/pure/__retain + local.set $1 + local.get $0 + call $~lib/rt/pure/__retain + local.set $5 + local.get $1 + call $~lib/rt/pure/__retain + local.set $4 + local.get $2 + local.set $3 + i32.const 0 + drop + local.get $3 + i32.const 0 + i32.lt_s + if + i32.const 928 + i32.const 992 + i32.const 1774 + i32.const 19 + call $~lib/builtins/abort + unreachable + end + local.get $4 + call $~lib/typedarray/Float64Array#get:length + local.get $3 + i32.add + local.get $5 + call $~lib/typedarray/Float64Array#get:length + i32.gt_s + if + i32.const 928 + i32.const 992 + i32.const 1775 + i32.const 47 + call $~lib/builtins/abort + unreachable + end + i32.const 0 + i32.const 0 + i32.eq + if (result i32) + i32.const 3 + i32.const 3 + i32.eq + else + i32.const 0 + end + if (result i32) + i32.const 0 + if (result i32) + i32.const 0 + else + i32.const 0 + end + i32.eqz + else + i32.const 0 + end + drop + local.get $5 + i32.load offset=4 + local.get $3 + i32.const 3 + i32.shl + i32.add + local.get $4 + i32.load offset=4 + local.get $4 + i32.load offset=8 + call $~lib/memory/memory.copy + local.get $4 + call $~lib/rt/pure/__release + local.get $5 + call $~lib/rt/pure/__release + local.get $1 + call $~lib/rt/pure/__release + ) + (func $assembly/index/correctPinkNoise (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + local.get $0 + call $~lib/rt/pure/__retain + local.set $0 + i32.const 0 + global.get $assembly/index/DAT_LEN + call $~lib/typedarray/Float64Array#constructor + local.set $1 + i32.const 0 + local.set $2 + loop $for-loop|0 + local.get $2 + global.get $assembly/index/HLF_LEN + i32.lt_s + local.set $3 + local.get $3 + if + local.get $1 + local.get $2 + local.get $0 + local.get $2 + call $~lib/typedarray/Float64Array#__get + global.get $assembly/index/pinkNoise + local.get $2 + call $~lib/array/Array#__get + f64.div + call $~lib/typedarray/Float64Array#__set + local.get $1 + global.get $assembly/index/HLF_LEN + local.get $2 + i32.add + local.get $0 + global.get $assembly/index/HLF_LEN + local.get $2 + i32.add + call $~lib/typedarray/Float64Array#__get + global.get $assembly/index/pinkNoise + local.get $2 + call $~lib/array/Array#__get + f64.div + call $~lib/typedarray/Float64Array#__set + local.get $2 + i32.const 1 + i32.add + local.set $2 + br $for-loop|0 + end + end + local.get $0 + local.get $1 + i32.const 0 + call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> + local.get $0 + call $~lib/rt/pure/__release + local.get $1 + call $~lib/rt/pure/__release + ) + (func $assembly/index/stereoToMono (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $0 + call $~lib/rt/pure/__retain + local.set $0 + i32.const 0 + global.get $assembly/index/DAT_LEN + call $~lib/typedarray/Float64Array#constructor + local.set $1 + i32.const 0 + local.set $2 + i32.const 0 + local.set $3 + loop $for-loop|0 + local.get $3 + global.get $assembly/index/HLF_LEN + i32.lt_s + local.set $4 + local.get $4 + if + local.get $1 + local.get $2 + local.tee $5 + i32.const 1 + i32.add + local.set $2 + local.get $5 + local.get $0 + local.get $3 + call $~lib/typedarray/Float64Array#__get + call $~lib/typedarray/Float64Array#__set + local.get $1 + local.get $2 + local.tee $5 + i32.const 1 + i32.add + local.set $2 + local.get $5 + local.get $0 + global.get $assembly/index/HLF_LEN + local.get $3 + i32.add + call $~lib/typedarray/Float64Array#__get + call $~lib/typedarray/Float64Array#__set + local.get $3 + i32.const 1 + i32.add + local.set $3 + br $for-loop|0 + end + end + local.get $0 + local.get $1 + i32.const 0 + call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> + local.get $0 + call $~lib/rt/pure/__release + local.get $1 + call $~lib/rt/pure/__release + ) + (func $assembly/index/invertAll (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 f64) + local.get $0 + call $~lib/rt/pure/__retain + local.set $0 + i32.const 0 + local.set $1 + loop $for-loop|0 + local.get $1 + global.get $assembly/index/HLF_LEN + i32.lt_s + local.set $2 + local.get $2 + if + local.get $0 + local.get $1 + call $~lib/typedarray/Float64Array#__get + local.set $3 + local.get $0 + local.get $1 + local.get $0 + global.get $assembly/index/DAT_LEN + local.get $1 + i32.sub + call $~lib/typedarray/Float64Array#__get + call $~lib/typedarray/Float64Array#__set + local.get $0 + global.get $assembly/index/DAT_LEN + local.get $1 + i32.sub + local.get $3 + call $~lib/typedarray/Float64Array#__set + local.get $1 + i32.const 1 + i32.add + local.set $1 + br $for-loop|0 + end + end + local.get $0 + call $~lib/rt/pure/__release + ) + (func $assembly/index/invertFirst (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 f64) + local.get $0 + call $~lib/rt/pure/__retain + local.set $0 + i32.const 0 + local.set $1 + loop $for-loop|0 + local.get $1 + global.get $assembly/index/QRT_LEN + i32.lt_s + local.set $2 + local.get $2 + if + local.get $0 + local.get $1 + call $~lib/typedarray/Float64Array#__get + local.set $3 + local.get $0 + local.get $1 + local.get $0 + global.get $assembly/index/HLF_LEN + local.get $1 + i32.sub + call $~lib/typedarray/Float64Array#__get + call $~lib/typedarray/Float64Array#__set + local.get $0 + global.get $assembly/index/HLF_LEN + local.get $1 + i32.sub + local.get $3 + call $~lib/typedarray/Float64Array#__set + local.get $1 + i32.const 1 + i32.add + local.set $1 + br $for-loop|0 + end + end + local.get $0 + call $~lib/rt/pure/__release + ) + (func $assembly/index/invertSecond (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 f64) + local.get $0 + call $~lib/rt/pure/__retain + local.set $0 + i32.const 0 + local.set $1 + loop $for-loop|0 + local.get $1 + global.get $assembly/index/QRT_LEN + i32.lt_s + local.set $2 + local.get $2 + if + local.get $0 + global.get $assembly/index/HLF_LEN + local.get $1 + i32.add + call $~lib/typedarray/Float64Array#__get + local.set $3 + local.get $0 + global.get $assembly/index/HLF_LEN + local.get $1 + i32.add + local.get $0 + global.get $assembly/index/DAT_LEN + local.get $1 + i32.sub + call $~lib/typedarray/Float64Array#__get + call $~lib/typedarray/Float64Array#__set + local.get $0 + global.get $assembly/index/DAT_LEN + local.get $1 + i32.sub + local.get $3 + call $~lib/typedarray/Float64Array#__set + local.get $1 + i32.const 1 + i32.add + local.set $1 + br $for-loop|0 + end + end + local.get $0 + call $~lib/rt/pure/__release + ) + (func $~lib/math/NativeMath.pow (param $0 f64) (param $1 f64) (result f64) + (local $2 f64) + (local $3 f64) + (local $4 i32) + (local $5 i64) + (local $6 i64) + (local $7 i64) + (local $8 i64) + (local $9 i64) + (local $10 f64) + (local $11 i64) + (local $12 i32) + (local $13 i64) + (local $14 i64) + (local $15 f64) + (local $16 f64) + (local $17 f64) + (local $18 f64) + (local $19 f64) + (local $20 f64) + (local $21 f64) + (local $22 f64) + (local $23 f64) + (local $24 f64) + (local $25 f64) + (local $26 f64) + (local $27 f64) + (local $28 f64) + (local $29 f64) + (local $30 f64) + (local $31 f64) + (local $32 f64) + (local $33 f64) + (local $34 f64) + (local $35 f64) + (local $36 f64) + (local $37 f64) + (local $38 f64) + (local $39 i32) + (local $40 i32) + (local $41 i32) + (local $42 i32) + (local $43 i64) + (local $44 i64) + local.get $1 + f64.abs + f64.const 2 + f64.le + if + local.get $1 + f64.const 2 + f64.eq + if + local.get $0 + local.get $0 + f64.mul + return + end + local.get $1 + f64.const 0.5 + f64.eq + if + local.get $0 + f64.sqrt + f64.abs + f64.const inf + local.get $0 + f64.const inf + f64.neg + f64.ne + select + return + end + local.get $1 + f64.const -1 + f64.eq + if + f64.const 1 + local.get $0 + f64.div + return + end + local.get $1 + f64.const 1 + f64.eq + if + local.get $0 + return + end + local.get $1 + f64.const 0 + f64.eq + if + f64.const 1 + return + end + end + i32.const 0 + i32.const 1 + i32.lt_s + drop + block $~lib/util/math/pow_lut|inlined.0 (result f64) + local.get $0 + local.set $3 + local.get $1 + local.set $2 + i32.const 0 + local.set $4 + local.get $3 + i64.reinterpret_f64 + local.set $5 + local.get $2 + i64.reinterpret_f64 + local.set $6 + local.get $5 + i64.const 52 + i64.shr_u + local.set $7 + local.get $6 + i64.const 52 + i64.shr_u + local.set $8 + local.get $7 + i64.const 1 + i64.sub + i64.const 2047 + i64.const 1 + i64.sub + i64.ge_u + if (result i32) + i32.const 1 + else + local.get $8 + i64.const 2047 + i64.and + i64.const 958 + i64.sub + i64.const 1086 + i64.const 958 + i64.sub + i64.ge_u + end + if + local.get $6 + local.set $9 + local.get $9 + i64.const 1 + i64.shl + i64.const 1 + i64.sub + i64.const -9007199254740992 + i64.const 1 + i64.sub + i64.ge_u + if + local.get $6 + i64.const 1 + i64.shl + i64.const 0 + i64.eq + if + f64.const 1 + br $~lib/util/math/pow_lut|inlined.0 + end + local.get $5 + i64.const 4607182418800017408 + i64.eq + if + f64.const nan:0x8000000000000 + br $~lib/util/math/pow_lut|inlined.0 + end + local.get $5 + i64.const 1 + i64.shl + i64.const -9007199254740992 + i64.gt_u + if (result i32) + i32.const 1 + else + local.get $6 + i64.const 1 + i64.shl + i64.const -9007199254740992 + i64.gt_u + end + if + local.get $3 + local.get $2 + f64.add + br $~lib/util/math/pow_lut|inlined.0 + end + local.get $5 + i64.const 1 + i64.shl + i64.const 9214364837600034816 + i64.eq + if + f64.const nan:0x8000000000000 + br $~lib/util/math/pow_lut|inlined.0 + end + local.get $5 + i64.const 1 + i64.shl + i64.const 9214364837600034816 + i64.lt_u + local.get $6 + i64.const 63 + i64.shr_u + i64.const 0 + i64.ne + i32.eqz + i32.eq + if + f64.const 0 + br $~lib/util/math/pow_lut|inlined.0 + end + local.get $2 + local.get $2 + f64.mul + br $~lib/util/math/pow_lut|inlined.0 + end + local.get $5 + local.set $9 + local.get $9 + i64.const 1 + i64.shl + i64.const 1 + i64.sub + i64.const -9007199254740992 + i64.const 1 + i64.sub + i64.ge_u + if + local.get $3 + local.get $3 + f64.mul + local.set $10 + local.get $5 + i64.const 63 + i64.shr_u + i32.wrap_i64 + if (result i32) + block $~lib/util/math/checkint|inlined.0 (result i32) + local.get $6 + local.set $9 + local.get $9 + i64.const 52 + i64.shr_u + i64.const 2047 + i64.and + local.set $11 + local.get $11 + i64.const 1023 + i64.lt_u + if + i32.const 0 + br $~lib/util/math/checkint|inlined.0 + end + local.get $11 + i64.const 1023 + i64.const 52 + i64.add + i64.gt_u + if + i32.const 2 + br $~lib/util/math/checkint|inlined.0 + end + i64.const 1 + i64.const 1023 + i64.const 52 + i64.add + local.get $11 + i64.sub + i64.shl + local.set $11 + local.get $9 + local.get $11 + i64.const 1 + i64.sub + i64.and + i64.const 0 + i64.ne + if + i32.const 0 + br $~lib/util/math/checkint|inlined.0 + end + local.get $9 + local.get $11 + i64.and + i64.const 0 + i64.ne + if + i32.const 1 + br $~lib/util/math/checkint|inlined.0 + end + i32.const 2 + end + i32.const 1 + i32.eq + else + i32.const 0 + end + if + local.get $10 + f64.neg + local.set $10 + end + local.get $6 + i64.const 63 + i64.shr_u + i64.const 0 + i64.ne + if (result f64) + f64.const 1 + local.get $10 + f64.div + else + local.get $10 + end + br $~lib/util/math/pow_lut|inlined.0 + end + local.get $5 + i64.const 63 + i64.shr_u + i64.const 0 + i64.ne + if + block $~lib/util/math/checkint|inlined.1 (result i32) + local.get $6 + local.set $9 + local.get $9 + i64.const 52 + i64.shr_u + i64.const 2047 + i64.and + local.set $11 + local.get $11 + i64.const 1023 + i64.lt_u + if + i32.const 0 + br $~lib/util/math/checkint|inlined.1 + end + local.get $11 + i64.const 1023 + i64.const 52 + i64.add + i64.gt_u + if + i32.const 2 + br $~lib/util/math/checkint|inlined.1 + end + i64.const 1 + i64.const 1023 + i64.const 52 + i64.add + local.get $11 + i64.sub + i64.shl + local.set $11 + local.get $9 + local.get $11 + i64.const 1 + i64.sub + i64.and + i64.const 0 + i64.ne + if + i32.const 0 + br $~lib/util/math/checkint|inlined.1 + end + local.get $9 + local.get $11 + i64.and + i64.const 0 + i64.ne + if + i32.const 1 + br $~lib/util/math/checkint|inlined.1 + end + i32.const 2 + end + local.set $12 + local.get $12 + i32.const 0 + i32.eq + if + local.get $3 + local.get $3 + f64.sub + local.get $3 + local.get $3 + f64.sub + f64.div + br $~lib/util/math/pow_lut|inlined.0 + end + local.get $12 + i32.const 1 + i32.eq + if + i32.const 262144 + local.set $4 + end + local.get $5 + i64.const 9223372036854775807 + i64.and + local.set $5 + local.get $7 + i64.const 2047 + i64.and + local.set $7 + end + local.get $8 + i64.const 2047 + i64.and + i64.const 958 + i64.sub + i64.const 1086 + i64.const 958 + i64.sub + i64.ge_u + if + local.get $5 + i64.const 4607182418800017408 + i64.eq + if + f64.const 1 + br $~lib/util/math/pow_lut|inlined.0 + end + local.get $8 + i64.const 2047 + i64.and + i64.const 958 + i64.lt_u + if + f64.const 1 + br $~lib/util/math/pow_lut|inlined.0 + end + local.get $5 + i64.const 4607182418800017408 + i64.gt_u + local.get $8 + i64.const 2048 + i64.lt_u + i32.eq + if (result f64) + f64.const inf + else + f64.const 0 + end + br $~lib/util/math/pow_lut|inlined.0 + end + local.get $7 + i64.const 0 + i64.eq + if + local.get $3 + f64.const 4503599627370496 + f64.mul + i64.reinterpret_f64 + local.set $5 + local.get $5 + i64.const 9223372036854775807 + i64.and + local.set $5 + local.get $5 + i64.const 52 + i64.const 52 + i64.shl + i64.sub + local.set $5 + end + end + local.get $5 + local.set $9 + local.get $9 + i64.const 4604531861337669632 + i64.sub + local.set $11 + local.get $11 + i64.const 52 + i64.const 7 + i64.sub + i64.shr_u + i64.const 127 + i64.and + i32.wrap_i64 + local.set $12 + local.get $11 + i64.const 52 + i64.shr_s + local.set $13 + local.get $9 + local.get $11 + i64.const 4095 + i64.const 52 + i64.shl + i64.and + i64.sub + local.set $14 + local.get $14 + f64.reinterpret_i64 + local.set $10 + local.get $13 + f64.convert_i64_s + local.set $15 + i32.const 1088 + local.get $12 + i32.const 2 + i32.const 3 + i32.add + i32.shl + i32.add + f64.load + local.set $16 + i32.const 1088 + local.get $12 + i32.const 2 + i32.const 3 + i32.add + i32.shl + i32.add + f64.load offset=16 + local.set $17 + i32.const 1088 + local.get $12 + i32.const 2 + i32.const 3 + i32.add + i32.shl + i32.add + f64.load offset=24 + local.set $18 + local.get $14 + i64.const 2147483648 + i64.add + i64.const -4294967296 + i64.and + f64.reinterpret_i64 + local.set $19 + local.get $10 + local.get $19 + f64.sub + local.set $20 + local.get $19 + local.get $16 + f64.mul + f64.const 1 + f64.sub + local.set $21 + local.get $20 + local.get $16 + f64.mul + local.set $22 + local.get $21 + local.get $22 + f64.add + local.set $23 + local.get $15 + f64.const 0.6931471805598903 + f64.mul + local.get $17 + f64.add + local.set $24 + local.get $24 + local.get $23 + f64.add + local.set $25 + local.get $15 + f64.const 5.497923018708371e-14 + f64.mul + local.get $18 + f64.add + local.set $26 + local.get $24 + local.get $25 + f64.sub + local.get $23 + f64.add + local.set $27 + f64.const -0.5 + local.get $23 + f64.mul + local.set $28 + local.get $23 + local.get $28 + f64.mul + local.set $29 + local.get $23 + local.get $29 + f64.mul + local.set $30 + f64.const -0.5 + local.get $21 + f64.mul + local.set $31 + local.get $21 + local.get $31 + f64.mul + local.set $32 + local.get $25 + local.get $32 + f64.add + local.set $33 + local.get $22 + local.get $28 + local.get $31 + f64.add + f64.mul + local.set $34 + local.get $25 + local.get $33 + f64.sub + local.get $32 + f64.add + local.set $35 + local.get $30 + f64.const -0.6666666666666679 + local.get $23 + f64.const 0.5000000000000007 + f64.mul + f64.add + local.get $29 + f64.const 0.7999999995323976 + local.get $23 + f64.const -0.6666666663487739 + f64.mul + f64.add + local.get $29 + f64.const -1.142909628459501 + local.get $23 + f64.const 1.0000415263675542 + f64.mul + f64.add + f64.mul + f64.add + f64.mul + f64.add + f64.mul + local.set $36 + local.get $26 + local.get $27 + f64.add + local.get $34 + f64.add + local.get $35 + f64.add + local.get $36 + f64.add + local.set $37 + local.get $33 + local.get $37 + f64.add + local.set $38 + local.get $33 + local.get $38 + f64.sub + local.get $37 + f64.add + global.set $~lib/util/math/log_tail + local.get $38 + local.set $38 + global.get $~lib/util/math/log_tail + local.set $37 + local.get $6 + i64.const -134217728 + i64.and + f64.reinterpret_i64 + local.set $34 + local.get $2 + local.get $34 + f64.sub + local.set $33 + local.get $38 + i64.reinterpret_f64 + i64.const -134217728 + i64.and + f64.reinterpret_i64 + local.set $32 + local.get $38 + local.get $32 + f64.sub + local.get $37 + f64.add + local.set $31 + local.get $34 + local.get $32 + f64.mul + local.set $36 + local.get $33 + local.get $32 + f64.mul + local.get $2 + local.get $31 + f64.mul + f64.add + local.set $35 + block $~lib/util/math/exp_inline|inlined.0 (result f64) + local.get $36 + local.set $15 + local.get $35 + local.set $10 + local.get $4 + local.set $12 + local.get $15 + i64.reinterpret_f64 + local.set $9 + local.get $9 + i64.const 52 + i64.shr_u + i32.wrap_i64 + i32.const 2047 + i32.and + local.set $39 + local.get $39 + i32.const 969 + i32.sub + i32.const 63 + i32.ge_u + if + local.get $39 + i32.const 969 + i32.sub + i32.const -2147483648 + i32.ge_u + if + f64.const -1 + f64.const 1 + local.get $12 + select + br $~lib/util/math/exp_inline|inlined.0 + end + local.get $39 + i32.const 1033 + i32.ge_u + if + local.get $9 + i64.const 63 + i64.shr_u + i64.const 0 + i64.ne + if (result f64) + local.get $12 + local.set $41 + local.get $41 + local.set $42 + i64.const 1152921504606846976 + f64.reinterpret_i64 + local.set $16 + local.get $16 + f64.neg + local.get $16 + local.get $42 + select + local.get $16 + f64.mul + else + local.get $12 + local.set $42 + local.get $42 + local.set $41 + i64.const 8070450532247928832 + f64.reinterpret_i64 + local.set $17 + local.get $17 + f64.neg + local.get $17 + local.get $41 + select + local.get $17 + f64.mul + end + br $~lib/util/math/exp_inline|inlined.0 + end + i32.const 0 + local.set $39 + end + f64.const 184.6649652337873 + local.get $15 + f64.mul + local.set $29 + local.get $29 + f64.const 6755399441055744 + f64.add + local.set $30 + local.get $30 + i64.reinterpret_f64 + local.set $14 + local.get $30 + f64.const 6755399441055744 + f64.sub + local.set $30 + local.get $15 + local.get $30 + f64.const -0.005415212348111709 + f64.mul + f64.add + local.get $30 + f64.const -1.2864023111638346e-14 + f64.mul + f64.add + local.set $28 + local.get $28 + local.get $10 + f64.add + local.set $28 + local.get $14 + i64.const 127 + i64.and + i64.const 1 + i64.shl + i32.wrap_i64 + local.set $40 + local.get $14 + local.get $12 + i64.extend_i32_u + i64.add + i64.const 52 + i64.const 7 + i64.sub + i64.shl + local.set $13 + i32.const 5184 + local.get $40 + i32.const 3 + i32.shl + i32.add + i64.load + f64.reinterpret_i64 + local.set $25 + i32.const 5184 + local.get $40 + i32.const 3 + i32.shl + i32.add + i64.load offset=8 + local.get $13 + i64.add + local.set $11 + local.get $28 + local.get $28 + f64.mul + local.set $27 + local.get $25 + local.get $28 + f64.add + local.get $27 + f64.const 0.49999999999996786 + local.get $28 + f64.const 0.16666666666665886 + f64.mul + f64.add + f64.mul + f64.add + local.get $27 + local.get $27 + f64.mul + f64.const 0.0416666808410674 + local.get $28 + f64.const 0.008333335853059549 + f64.mul + f64.add + f64.mul + f64.add + local.set $24 + local.get $39 + i32.const 0 + i32.eq + if + block $~lib/util/math/specialcase|inlined.0 (result f64) + local.get $24 + local.set $18 + local.get $11 + local.set $44 + local.get $14 + local.set $43 + local.get $43 + i64.const 2147483648 + i64.and + i64.const 0 + i64.ne + i32.eqz + if + local.get $44 + i64.const 1009 + i64.const 52 + i64.shl + i64.sub + local.set $44 + local.get $44 + f64.reinterpret_i64 + local.set $17 + f64.const 5486124068793688683255936e279 + local.get $17 + local.get $17 + local.get $18 + f64.mul + f64.add + f64.mul + br $~lib/util/math/specialcase|inlined.0 + end + local.get $44 + i64.const 1022 + i64.const 52 + i64.shl + i64.add + local.set $44 + local.get $44 + f64.reinterpret_i64 + local.set $17 + local.get $17 + local.get $17 + local.get $18 + f64.mul + f64.add + local.set $16 + local.get $16 + f64.abs + f64.const 1 + f64.lt + if + f64.const 1 + local.get $16 + f64.copysign + local.set $23 + local.get $17 + local.get $16 + f64.sub + local.get $17 + local.get $18 + f64.mul + f64.add + local.set $22 + local.get $23 + local.get $16 + f64.add + local.set $21 + local.get $23 + local.get $21 + f64.sub + local.get $16 + f64.add + local.get $22 + f64.add + local.set $22 + local.get $21 + local.get $22 + f64.add + local.get $23 + f64.sub + local.set $16 + local.get $16 + f64.const 0 + f64.eq + if + local.get $44 + i64.const -9223372036854775808 + i64.and + f64.reinterpret_i64 + local.set $16 + end + end + local.get $16 + f64.const 2.2250738585072014e-308 + f64.mul + end + br $~lib/util/math/exp_inline|inlined.0 + end + local.get $11 + f64.reinterpret_i64 + local.set $26 + local.get $26 + local.get $26 + local.get $24 + f64.mul + f64.add + end + end + return + ) + (func $assembly/index/peakFilter (param $0 i32) (param $1 f64) + (local $2 f64) + (local $3 f64) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 f64) + local.get $0 + call $~lib/rt/pure/__retain + local.set $0 + f64.const 0 + local.set $2 + f64.const 0 + local.set $3 + i32.const 0 + global.get $assembly/index/DAT_LEN + call $~lib/typedarray/Float64Array#constructor + local.set $4 + i32.const 0 + local.set $5 + loop $for-loop|0 + local.get $5 + global.get $assembly/index/DAT_LEN + i32.lt_s + local.set $6 + local.get $6 + if + local.get $0 + local.get $5 + call $~lib/typedarray/Float64Array#__get + local.get $2 + f64.gt + if + local.get $0 + local.get $5 + call $~lib/typedarray/Float64Array#__get + local.set $2 + end + local.get $4 + local.get $5 + local.get $0 + local.get $5 + call $~lib/typedarray/Float64Array#__get + local.get $1 + f64.mul + local.get $1 + call $~lib/math/NativeMath.pow + call $~lib/typedarray/Float64Array#__set + local.get $4 + local.get $5 + call $~lib/typedarray/Float64Array#__get + local.get $3 + f64.gt + if + local.get $4 + local.get $5 + call $~lib/typedarray/Float64Array#__get + local.set $3 + end + local.get $5 + i32.const 1 + i32.add + local.set $5 + br $for-loop|0 + end + end + local.get $3 + local.get $2 + f64.div + local.set $7 + i32.const 0 + local.set $5 + loop $for-loop|1 + local.get $5 + global.get $assembly/index/DAT_LEN + i32.lt_s + local.set $6 + local.get $6 + if + local.get $0 + local.get $5 + local.get $4 + local.get $5 + call $~lib/typedarray/Float64Array#__get + local.get $7 + f64.div + call $~lib/typedarray/Float64Array#__set + local.get $5 + i32.const 1 + i32.add + local.set $5 + br $for-loop|1 + end + end + local.get $0 + call $~lib/rt/pure/__release + local.get $4 + call $~lib/rt/pure/__release + ) + (func $assembly/index/smoothArray (param $0 i32) (param $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 f64) + (local $6 i32) + (local $7 i32) + (local $8 i32) + local.get $0 + call $~lib/rt/pure/__retain + local.set $0 + i32.const 0 + global.get $assembly/index/DAT_LEN + call $~lib/typedarray/Float64Array#constructor + local.set $2 + i32.const 0 + local.set $3 + loop $for-loop|0 + local.get $3 + global.get $assembly/index/DAT_LEN + i32.lt_s + local.set $4 + local.get $4 + if + f64.const 0 + local.set $5 + local.get $3 + local.get $1 + i32.sub + local.set $6 + loop $for-loop|1 + local.get $6 + local.get $3 + local.get $1 + i32.add + i32.le_s + local.set $7 + local.get $7 + if + local.get $6 + local.set $8 + local.get $8 + i32.const 0 + i32.lt_s + if + local.get $8 + global.get $assembly/index/DAT_LEN + i32.add + local.set $8 + end + local.get $5 + local.get $0 + local.get $8 + global.get $assembly/index/DAT_LEN + i32.rem_s + call $~lib/typedarray/Float64Array#__get + f64.add + local.set $5 + local.get $6 + i32.const 1 + i32.add + local.set $6 + br $for-loop|1 + end + end + local.get $2 + local.get $3 + local.get $5 + local.get $1 + i32.const 2 + i32.mul + i32.const 1 + i32.add + f64.convert_i32_s + f64.div + call $~lib/typedarray/Float64Array#__set + local.get $3 + i32.const 1 + i32.add + local.set $3 + br $for-loop|0 + end + end + local.get $0 + local.get $2 + i32.const 0 + call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> + local.get $0 + call $~lib/rt/pure/__release + local.get $2 + call $~lib/rt/pure/__release + ) + (func $assembly/index/applyValueLeveling (param $0 i32) (param $1 i32) (param $2 f64) (param $3 f64) + (local $4 i32) + (local $5 i32) + (local $6 f64) + (local $7 f64) + local.get $0 + call $~lib/rt/pure/__retain + local.set $0 + local.get $1 + call $~lib/rt/pure/__retain + local.set $1 + i32.const 0 + local.set $4 + loop $for-loop|0 + local.get $4 + global.get $assembly/index/DAT_LEN + i32.lt_s + local.set $5 + local.get $5 + if + local.get $0 + local.get $4 + call $~lib/typedarray/Float64Array#__get + local.get $1 + local.get $4 + call $~lib/typedarray/Float64Array#__get + f64.sub + local.set $6 + f64.const 100 + local.get $6 + f64.const 0 + f64.gt + if (result f64) + local.get $2 + else + local.get $3 + end + f64.sub + local.set $7 + local.get $0 + local.get $4 + local.get $0 + local.get $4 + call $~lib/typedarray/Float64Array#__get + local.get $6 + local.get $7 + f64.mul + f64.const 100 + f64.div + f64.sub + call $~lib/typedarray/Float64Array#__set + local.get $4 + i32.const 1 + i32.add + local.set $4 + br $for-loop|0 + end + end + local.get $0 + call $~lib/rt/pure/__release + local.get $1 + call $~lib/rt/pure/__release + ) + (func $~lib/rt/__newBuffer (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + local.get $0 + local.get $1 + call $~lib/rt/pure/__new + local.set $3 + local.get $2 + if + local.get $3 + local.get $2 + local.get $0 + call $~lib/memory/memory.copy + end + local.get $3 + ) + (func $~lib/rt/__newArray (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + i32.const 16 + local.get $2 + call $~lib/rt/pure/__new + local.set $4 + local.get $0 + local.get $1 + i32.shl + local.set $5 + local.get $5 + i32.const 0 + local.get $3 + call $~lib/rt/__newBuffer + local.set $6 + local.get $4 + local.get $6 + call $~lib/rt/pure/__retain + i32.store + local.get $4 + local.get $6 + i32.store offset=4 + local.get $4 + local.get $5 + i32.store offset=8 + local.get $4 + local.get $0 + i32.store offset=12 + local.get $4 + ) + (func $~lib/array/Array#get:length (param $0 i32) (result i32) + local.get $0 + i32.load offset=12 + ) + (func $~lib/typedarray/Float64Array#set<~lib/array/Array> (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $1 + call $~lib/rt/pure/__retain + local.set $1 + local.get $0 + call $~lib/rt/pure/__retain + local.set $5 + local.get $1 + call $~lib/rt/pure/__retain + local.set $4 + local.get $2 + local.set $3 + i32.const 0 + drop + local.get $3 + i32.const 0 + i32.lt_s + if + i32.const 928 + i32.const 992 + i32.const 1774 + i32.const 19 + call $~lib/builtins/abort + unreachable + end + local.get $4 + call $~lib/array/Array#get:length + local.get $3 + i32.add + local.get $5 + call $~lib/typedarray/Float64Array#get:length + i32.gt_s + if + i32.const 928 + i32.const 992 + i32.const 1775 + i32.const 47 + call $~lib/builtins/abort + unreachable + end + i32.const 0 + i32.const 0 + i32.eq + if (result i32) + i32.const 3 + i32.const 3 + i32.eq + else + i32.const 0 + end + if (result i32) + i32.const 0 + if (result i32) + i32.const 0 + else + i32.const 0 + end + i32.eqz + else + i32.const 0 + end + drop + local.get $5 + i32.load offset=4 + local.get $3 + i32.const 3 + i32.shl + i32.add + local.get $4 + i32.load offset=4 + local.get $4 + i32.load offset=8 + call $~lib/memory/memory.copy + local.get $4 + call $~lib/rt/pure/__release + local.get $5 + call $~lib/rt/pure/__release + local.get $1 + call $~lib/rt/pure/__release + ) + (func $~lib/gc/gc.collect + call $~lib/rt/pure/__collect + ) + (func $assembly/index/update (param $0 i32) + (local $1 f64) + (local $2 f64) + (local $3 f64) + (local $4 f64) + (local $5 f64) + (local $6 f64) + (local $7 i32) + (local $8 i32) + (local $9 f64) + (local $10 f64) + (local $11 f64) + (local $12 f64) + (local $13 f64) + (local $14 i32) + local.get $0 + call $~lib/rt/pure/__retain + local.set $0 + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.equalize + call $~lib/typedarray/Float64Array#__get + call $assembly/index/isOn + if + local.get $0 + call $assembly/index/correctPinkNoise + end + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.mono_audio + call $~lib/typedarray/Float64Array#__get + call $assembly/index/isOn + if + local.get $0 + call $assembly/index/stereoToMono + end + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.audio_direction + call $~lib/typedarray/Float64Array#__get + call $assembly/index/isOn + if + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.mono_audio + call $~lib/typedarray/Float64Array#__get + call $assembly/index/isOn + if + local.get $0 + call $assembly/index/invertAll + else + local.get $0 + call $assembly/index/invertFirst + end + else + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.mono_audio + call $~lib/typedarray/Float64Array#__get + call $assembly/index/isOn + if + local.get $0 + call $assembly/index/invertSecond + end + end + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.peak_filter + call $~lib/typedarray/Float64Array#__get + call $assembly/index/isOn + if + local.get $0 + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.peak_filter + call $~lib/typedarray/Float64Array#__get + f64.const 1 + f64.add + call $assembly/index/peakFilter + end + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.value_smoothing + call $~lib/typedarray/Float64Array#__get + call $assembly/index/isOn + if + local.get $0 + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.value_smoothing + call $~lib/typedarray/Float64Array#__get + i32.trunc_f64_s + call $assembly/index/smoothArray + end + global.get $assembly/index/audioData + if + local.get $0 + global.get $assembly/index/audioData + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.audio_increase + call $~lib/typedarray/Float64Array#__get + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.audio_decrease + call $~lib/typedarray/Float64Array#__get + call $assembly/index/applyValueLeveling + end + f64.const 0 + local.set $1 + f64.const 1 + local.set $2 + f64.const 0 + local.set $3 + f64.const 0 + local.set $4 + f64.const 0 + local.set $5 + f64.const 0 + local.set $6 + i32.const 0 + local.set $7 + loop $for-loop|0 + local.get $7 + global.get $assembly/index/DAT_LEN + i32.lt_s + local.set $8 + local.get $8 + if + local.get $0 + local.get $7 + call $~lib/typedarray/Float64Array#__get + local.set $9 + local.get $9 + local.get $2 + f64.lt + if + local.get $9 + local.set $2 + end + local.get $9 + local.get $3 + f64.gt + if + local.get $9 + local.set $3 + end + local.get $7 + i32.const 42 + i32.const 3 + i32.div_s + i32.lt_s + if + local.get $4 + local.get $9 + global.get $assembly/index/audioProps + global.get $assembly/index/Props.bass + call $~lib/typedarray/Float64Array#__get + f64.mul + f64.add + local.set $4 + else + local.get $7 + i32.const 69 + i32.gt_s + if + local.get $6 + local.get $9 + global.get $assembly/index/audioProps + global.get $assembly/index/Props.highs + call $~lib/typedarray/Float64Array#__get + f64.mul + f64.add + local.set $6 + else + local.get $5 + local.get $9 + global.get $assembly/index/audioProps + global.get $assembly/index/Props.mids + call $~lib/typedarray/Float64Array#__get + f64.mul + f64.add + local.set $5 + end + end + local.get $1 + local.get $9 + f64.add + local.set $1 + local.get $7 + i32.const 1 + i32.add + local.set $7 + br $for-loop|0 + end + end + local.get $1 + global.get $assembly/index/DAT_LEN + f64.convert_i32_s + f64.div + local.set $10 + local.get $3 + global.get $assembly/index/audioSettings + global.get $assembly/index/Sett.minimum_volume + call $~lib/typedarray/Float64Array#__get + f64.const 1e3 + f64.div + f64.lt + if (result f64) + f64.const 0.9999 + else + f64.const 0 + end + local.set $11 + local.get $4 + f64.const 8 + f64.mul + local.get $5 + f64.sub + local.get $6 + f64.add + f64.const 6 + f64.div + local.get $10 + f64.div + local.set $12 + local.get $3 + local.get $2 + f64.sub + local.set $13 + global.get $assembly/index/audioData + local.get $0 + i32.const 0 + call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> + global.get $assembly/index/audioProps + i32.const 10 + i32.const 3 + i32.const 3 + i32.const 0 + call $~lib/rt/__newArray + call $~lib/rt/pure/__retain + local.set $8 + local.get $8 + i32.load offset=4 + local.set $14 + local.get $14 + local.get $4 + f64.store + local.get $14 + local.get $5 + f64.store offset=8 + local.get $14 + local.get $6 + f64.store offset=16 + local.get $14 + local.get $1 + f64.store offset=24 + local.get $14 + local.get $2 + f64.store offset=32 + local.get $14 + local.get $3 + f64.store offset=40 + local.get $14 + local.get $10 + f64.store offset=48 + local.get $14 + local.get $13 + f64.store offset=56 + local.get $14 + local.get $11 + f64.store offset=64 + local.get $14 + local.get $12 + f64.store offset=72 + local.get $8 + local.tee $14 + i32.const 0 + call $~lib/typedarray/Float64Array#set<~lib/array/Array> + call $~lib/gc/gc.collect + local.get $14 + call $~lib/rt/pure/__release + local.get $0 + call $~lib/rt/pure/__release + ) + (func $~start + call $start:assembly/index + ) + (func $~lib/rt/pure/finalize (param $0 i32) + i32.const 0 + drop + global.get $~lib/rt/tlsf/ROOT + local.get $0 + call $~lib/rt/tlsf/freeBlock + ) + (func $~lib/rt/pure/decrement (param $0 i32) + (local $1 i32) + (local $2 i32) + local.get $0 + i32.load offset=4 + local.set $1 + local.get $1 + i32.const 268435455 + i32.and + local.set $2 + i32.const 0 + drop + i32.const 1 + drop + local.get $0 + i32.load + i32.const 1 + i32.and + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 96 + i32.const 122 + i32.const 14 + call $~lib/builtins/abort + unreachable + end + local.get $2 + i32.const 1 + i32.eq + if + local.get $0 + i32.const 20 + i32.add + i32.const 1 + call $~lib/rt/__visit_members + i32.const 1 + drop + i32.const 1 + drop + local.get $1 + i32.const -2147483648 + i32.and + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 96 + i32.const 126 + i32.const 18 + call $~lib/builtins/abort + unreachable + end + local.get $0 + call $~lib/rt/pure/finalize + else + i32.const 1 + drop + local.get $2 + i32.const 0 + i32.gt_u + i32.eqz + if + i32.const 0 + i32.const 96 + i32.const 136 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + i32.const 1 + drop + local.get $0 + local.get $1 + i32.const 268435455 + i32.const -1 + i32.xor + i32.and + local.get $2 + i32.const 1 + i32.sub + i32.or + i32.store offset=4 + end + ) + (func $~lib/rt/pure/__collect + i32.const 1 + drop + return + ) + (func $~lib/rt/pure/__visit (param $0 i32) (param $1 i32) + local.get $0 + global.get $~lib/memory/__heap_base + i32.lt_u + if + return + end + i32.const 1 + drop + i32.const 1 + drop + local.get $1 + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 96 + i32.const 69 + i32.const 16 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 20 + i32.sub + call $~lib/rt/pure/decrement + ) + (func $~lib/arraybuffer/ArrayBuffer~visit (param $0 i32) (param $1 i32) + nop + ) + (func $~lib/string/String~visit (param $0 i32) (param $1 i32) + nop + ) + (func $~lib/arraybuffer/ArrayBufferView~visit (param $0 i32) (param $1 i32) + (local $2 i32) + local.get $0 + i32.load + local.tee $2 + if + local.get $2 + local.get $1 + call $~lib/rt/pure/__visit + end + ) + (func $~lib/array/Array#__visit (param $0 i32) (param $1 i32) + i32.const 0 + drop + local.get $0 + i32.load + local.get $1 + call $~lib/rt/pure/__visit + ) + (func $~lib/array/Array~visit (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + call $~lib/array/Array#__visit + ) + (func $~lib/typedarray/Float64Array~visit (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + call $~lib/arraybuffer/ArrayBufferView~visit + ) + (func $~lib/typedarray/Uint8ClampedArray~visit (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + call $~lib/arraybuffer/ArrayBufferView~visit + ) + (func $~lib/array/Array#__visit (param $0 i32) (param $1 i32) + i32.const 0 + drop + local.get $0 + i32.load + local.get $1 + call $~lib/rt/pure/__visit + ) + (func $~lib/array/Array~visit (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + call $~lib/array/Array#__visit + ) + (func $~lib/rt/__visit_members (param $0 i32) (param $1 i32) + block $invalid + block $~lib/array/Array + block $~lib/typedarray/Uint8ClampedArray + block $~lib/typedarray/Float64Array + block $~lib/array/Array + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $~lib/array/Array $~lib/typedarray/Float64Array $~lib/typedarray/Uint8ClampedArray $~lib/array/Array $invalid + end + local.get $0 + local.get $1 + call $~lib/arraybuffer/ArrayBuffer~visit + return + end + local.get $0 + local.get $1 + call $~lib/string/String~visit + return + end + local.get $0 + local.get $1 + call $~lib/arraybuffer/ArrayBufferView~visit + return + end + local.get $0 + local.get $1 + call $~lib/array/Array~visit + return + end + local.get $0 + local.get $1 + call $~lib/typedarray/Float64Array~visit + return + end + local.get $0 + local.get $1 + call $~lib/typedarray/Uint8ClampedArray~visit + return + end + local.get $0 + local.get $1 + call $~lib/array/Array~visit + return + end + unreachable + ) +) diff --git a/src/wasm/index.js b/src/wasm/index.js new file mode 100644 index 0000000..04bc346 --- /dev/null +++ b/src/wasm/index.js @@ -0,0 +1,5 @@ +const fs = require("fs"); +const loader = require("@assemblyscript/loader"); +const imports = { /* imports go here */ }; +const wasmModule = loader.instantiateSync(fs.readFileSync(__dirname + "/build/optimized.wasm"), imports); +module.exports = wasmModule.exports; diff --git a/src/wasm/package-lock.json b/src/wasm/package-lock.json new file mode 100644 index 0000000..62991b7 --- /dev/null +++ b/src/wasm/package-lock.json @@ -0,0 +1,33 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@assemblyscript/loader": { + "version": "0.17.7", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.17.7.tgz", + "integrity": "sha512-b3EESdpAEA7XqrnCBmkiM7bGX8r8M++6igAjy1LrflK4L3H78tCpXlKaams7/OEbwaycmyZL05vf7Yo8xCWBWA==" + }, + "assemblyscript": { + "version": "0.17.7", + "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.17.7.tgz", + "integrity": "sha512-HyFy/9FAXO/Q1HclibD6OsTleMfHJd3+gQRQtjLd9CbFmNeINxaxFHYlmzES2nqeSk9Jqxs29OOQaq13yboG4A==", + "dev": true, + "requires": { + "binaryen": "98.0.0-nightly.20201109", + "long": "^4.0.0" + } + }, + "binaryen": { + "version": "98.0.0-nightly.20201109", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-98.0.0-nightly.20201109.tgz", + "integrity": "sha512-iRarAqdH5lMWlMBzrDuJgLYJR2g4QXk93iYE2zpr6gEZkb/jCgDpPUXdhuN11Ge1zZ/6By4DwA1mmifcx7FWaw==", + "dev": true + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + } + } +} diff --git a/src/wasm/package.json b/src/wasm/package.json new file mode 100644 index 0000000..7287ec5 --- /dev/null +++ b/src/wasm/package.json @@ -0,0 +1,14 @@ +{ + "scripts": { + "asbuild:untouched": "asc assembly/index.ts --target debug", + "asbuild:optimized": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized", + "test": "node tests" + }, + "dependencies": { + "@assemblyscript/loader": "^0.17.7" + }, + "devDependencies": { + "assemblyscript": "^0.17.7" + } +} diff --git a/src/wasm/tests/index.js b/src/wasm/tests/index.js new file mode 100644 index 0000000..df97f5c --- /dev/null +++ b/src/wasm/tests/index.js @@ -0,0 +1,4 @@ +const assert = require("assert"); +const myModule = require(".."); +assert.equal(myModule.add(1, 2), 3); +console.log("ok"); diff --git a/src/WEAS.ts b/src/weas/WEAS.ts similarity index 57% rename from src/WEAS.ts rename to src/weas/WEAS.ts index 22d36b4..e34f3f0 100644 --- a/src/WEAS.ts +++ b/src/weas/WEAS.ts @@ -23,13 +23,15 @@ * */ -import { CComponent } from "./CComponent"; -import { CSettings } from "./CSettings"; -import { Ready } from "./Ready"; -import { Smallog } from "./Smallog"; +import { CComponent } from "../CComponent"; +import { CSettings } from "../CSettings"; +import { Ready } from "../Ready"; +import { Smallog } from "../Smallog"; import WEASWorker from 'worker-loader!./WEASWorker'; +import wasmWorker from 'wasm-worker'; + export class WEASettings extends CSettings { audioprocessing: boolean = true; // do pink-noise processing? @@ -55,8 +57,6 @@ export class WEASettings extends CSettings { export class WEAS extends CComponent { - // audio processing worker - private weasWorker: WEASWorker = null; // last processed audio object public lastAudio = null; @@ -77,40 +77,56 @@ export class WEAS extends CComponent { return; } - // initialize web worker - this.weasWorker = new WEASWorker(); - var self = this; - // worker event data - this.weasWorker.onmessage = (e) => { - e.data.data = new Float32Array(e.data.data); - self.lastAudio = e.data; - Smallog.Debug("Got Data from Worker! Offset= " + (performance.now() / 1000 - e.data.time) + ", Data= " + JSON.stringify(e.data)); - }; - - // worker Error - this.weasWorker.onerror = (e) => { - Smallog.Error("weas error: [" + e.filename + ", Line: " + e.lineno + "] " + e.message); - }; - - window['wallpaperRegisterAudioListener']((audioArray) => { - // check proof - if (!audioArray) return; - if (audioArray.length != 128) { - Smallog.Error("audioListener: received invalid audio data array. Length: " + audioArray.length); - return; - } - let audBuff = new Float32Array(audioArray); - - Smallog.Debug("Sent Data to Worker: " + JSON.stringify(audioArray)); - // post web worker task - this.weasWorker.postMessage({ - settings: this.GetSettingsObj(), - last: Object.assign({}, this.lastAudio), - time: performance.now() / 1000, - audio: audBuff.buffer - }, [audBuff.buffer]); - }); + + wasmWorker('WEAS.wasm') + .then(module => { + + window['wallpaperRegisterAudioListener']((audioArray) => { + // check proof + if (!audioArray) return; + if (audioArray.length != 128) { + Smallog.Error("audioListener: received invalid audio data array. Length: " + audioArray.length); + return; + } + let audBuff = new Float32Array(audioArray); + Smallog.Debug("Send Data to Worker: " + JSON.stringify(audioArray)); + + // post web worker task + module.run(({ + importObject, + instance, + params + } + // the code ran in web worker + ) => { + //const sum = instance.exports.message(...params); + const sum = instance.exports.message(params.time); + return 'got time message: ' + sum; + }, + // the object posted to web worker + { + settings: this.GetSettingsObj(), + last: Object.assign({}, this.lastAudio), + time: performance.now() / 1000, + audio: audBuff.buffer + }) + // the result from web worker + .then(e => { + e.data.data = new Float32Array(e.data.data); + self.lastAudio = e.data; + Smallog.Debug("Got Data from Worker! Offset= " + (performance.now() / 1000 - e.data.time) + ", Data= " + JSON.stringify(e.data)); + }); + }); + + + }) + .catch(ex => { + // ex is a string that represents the exception + Smallog.Error("weas error: [" + ex.filename + ", Line: " + ex.lineno + "] " + ex.message); + }); + + } public hasAudio() { diff --git a/src/weas/WEAS.wasm.asc b/src/weas/WEAS.wasm.asc new file mode 100644 index 0000000..dc25421 --- /dev/null +++ b/src/weas/WEAS.wasm.asc @@ -0,0 +1,252 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Wallaper Engine Audio Supplier worker. + */ + +const DAT_LEN = 128; +const HLF_LEN = 64; +const QRT_LEN = 32; + +const pinkNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, + 0.5452348949654, 0.50723325864167, 0.4677726234682, 0.44204182748767, 0.41956517802157, + 0.41517375040002, 0.41312118577934, 0.40618363960446, 0.39913707474975, 0.38207008614508, + 0.38329789106488, 0.37472136606245, 0.36586428412968, 0.37603017335105, 0.39762590761573, + 0.39391828858591, 0.37930603769622, 0.39433365764563, 0.38511504613859, 0.39082579241834, + 0.3811852720504, 0.40231453727161, 0.40244151133175, 0.39965366884521, 0.39761103827545, + 0.51136400422212, 0.66151212038954, 0.66312205226679, 0.7416276690995, 0.74614971301133, + 0.84797007577483, 0.8573583910469, 0.96382997811663, 0.99819377577185, 1.0628692615814, + 1.1059083969751, 1.1819808497335, 1.257092297208, 1.3226521464753, 1.3735992532905, + 1.4953223705889, 1.5310064942373, 1.6193923584808, 1.7094805527135, 1.7706604552218, + 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, + 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, + 2.4043566176474, 2.4280476777842, 2.3917477397336, 2.4032522546622, 2.3614180150678]; + +// correct pink noise for first and second half +function correctPinkNoise(data: Float64Array): void { + var correct = new Float64Array(DAT_LEN); + for (var i = 0; i < HLF_LEN; i++) { + correct[i] = data[i] / pinkNoise[i]; + correct[HLF_LEN + i] = data[HLF_LEN + i] / pinkNoise[i]; + } + data.set(correct); +} + +// merge first and second half into full range +function stereoToMono(data: Float64Array): void { + var mono = new Float64Array(DAT_LEN); + var mIdx = 0; + for (var i = 0; i < HLF_LEN; i++) { + mono[mIdx++] = data[i]; + mono[mIdx++] = data[HLF_LEN + i]; + } + data.set(mono); +} + +// switch front with back in first half +function invertFirst(data: Float64Array): void { + for (var i = 0; i < QRT_LEN; i++) { + var a = data[i]; + data[i] = data[HLF_LEN - i]; + data[HLF_LEN - i] = a; + } +} + +// switch front with back in second half +function invertSecond(data: Float64Array): void { + for (var i = 0; i < QRT_LEN; i++) { + var b = data[HLF_LEN + i]; + data[HLF_LEN + i] = data[DAT_LEN - i]; + data[DAT_LEN - i] = b; + } +} + +// switch front with back in full range +function invertAll(data: Float64Array): void { + for (var i = 0; i < HLF_LEN; i++) { + var a = data[i]; + data[i] = data[DAT_LEN - i]; + data[DAT_LEN - i] = a; + } +} + +// filter peaks for full range +function peakFilter(array: Float64Array, amount: f64): void { + var oldMax: f64 = 0; + var newMax: f64 = 0; + var newArray = new Float64Array(DAT_LEN); + var i = 0; + // pow this shit + for (; i < DAT_LEN; i++) { + if (array[i] > oldMax) oldMax = array[i]; + newArray[i] = Math.pow(array[i] * amount, amount) as f64; + if (newArray[i] > newMax) newMax = newArray[i]; + } + // re-scale & apply + var divide: f64 = newMax / oldMax; + for (i = 0; i < DAT_LEN; i++) + array[i] = newArray[i] / divide; +} + +// smooth values for full range +function smoothArray(array: Float64Array, steps: i32): void { + var newArray = new Float64Array(DAT_LEN); + // make smoothed array + for (var outer = 0; outer < DAT_LEN; outer++) { + var sum: f64 = 0; + for (var inner = outer - steps; inner <= outer + steps; inner++) { + var idx = inner; + if (idx < 0) idx += DAT_LEN; + sum += array[idx % DAT_LEN]; + } + newArray[outer] = sum / (((steps * 2) + 1) as f64); + } + array.set(newArray); +} + +// function will apply setting-defined data smoothing +function applyValueLeveling(curr: Float64Array, prev: Float64Array, inc: f64, dec: f64): void { + for (var i = 0; i < DAT_LEN; i++) { + var diff: f64 = curr[i] - prev[i]; + var mlt: f64 = 100 - (diff > 0 ? inc : dec); + curr[i] -= diff * mlt / 100; + } +} + +// this will hold the current processed audio data +// either: B-H | H-B | HL-BL-BR-HR | BL-HL-HR-BR +// where ( B=bass, H=high, L=left, R=right ) +// in range > 0.0 and ~< 1.5 +export const audioData = new Float64Array(DAT_LEN); + +// this will hold the current processed properties: +export const audioProps = new Float64Array(10); +enum Props { + bass = 0, + mids = 1, + highs = 2, + sum = 3, + min = 4, + max = 5, + average = 6, + range = 7, + silent = 8, + intensity = 9 +} + + +// this will hold the current processing settings +export const audioSettings = new Float64Array(8); +enum Sett { + equalize = 0, + mono_audio = 1, + audio_direction = 2, + peak_filter = 3, + value_smoothing = 4, + audio_increase = 5, + audio_decrease = 6, + minimum_volume = 7 +} + +// check helper +function isOn(a: f64): boolean { + return a > 0; +} + +// this will update and process new data +export function update(newData: Float64Array): void { + + // fix pink noise? + if (isOn(audioSettings[Sett.equalize])) + correctPinkNoise(newData); + + // write botch channels to mono + if (isOn(audioSettings[Sett.mono_audio])) + stereoToMono(newData); + + if (isOn(audioSettings[Sett.audio_direction])) { + // flipped high & low mapping + if (isOn(audioSettings[Sett.mono_audio])) + // flip whole range + invertAll(newData); + else { + // only flip first half of stereo + invertFirst(newData); + } + + } + else { + // normal high & low mapping + if (isOn(audioSettings[Sett.mono_audio])) { + // only flip the second half of the data + invertSecond(newData); + } + } + + // process peaks? + if (isOn(audioSettings[Sett.peak_filter])) + peakFilter(newData, audioSettings[Sett.peak_filter] + 1); + + // smooth data? + if (isOn(audioSettings[Sett.value_smoothing])) + smoothArray(newData, audioSettings[Sett.value_smoothing] as i32); + + // process with last data? + if (audioData) + applyValueLeveling(newData, audioData, + audioSettings[Sett.audio_increase], + audioSettings[Sett.audio_decrease]); + + // process current frequency data and previous if given + var sum: f64 = 0, + min: f64 = 1, + max: f64 = 0, + bass: f64 = 0, + mids: f64 = 0, + peaks: f64 = 0; + + for (var indx = 0; indx < DAT_LEN; indx++) { + // parse current freq value + var idata: f64 = newData[indx]; + // process min max value + if (idata < min) min = idata; + if (idata > max) max = idata; + // process ranges + if (indx < (42 / 3)) bass += idata * audioProps[Props.bass]; + else if (indx > 69) peaks += idata * audioProps[Props.highs]; + else mids += idata * audioProps[Props.mids]; + // calc peak average + sum += idata; + } + + // calc average with previous entry + var average: f64 = sum / (DAT_LEN as f64); + var silent: f64 = (max < audioSettings[Sett.minimum_volume] / 1000) ? 0.9999 : 0.00; + var intensity: f64 = (bass * 8 - mids + peaks) / 6 / average; + var range: f64 = max - min; + + // Apply Data + audioData.set(newData); + audioProps.set([ + bass, + mids, + peaks, + sum, + min, + max, + average, + range, + silent, + intensity + ]); + + // clean + gc.collect(); +} + diff --git a/src/WEASWorker.ts b/src/weas/WEASWorker.ts similarity index 100% rename from src/WEASWorker.ts rename to src/weas/WEASWorker.ts From 27c35be34782062717b5794618a3c2097e87cb75 Mon Sep 17 00:00:00 2001 From: hexxone Date: Sun, 20 Dec 2020 11:06:11 +0100 Subject: [PATCH 13/76] Add custom wasc-worker --- .gitmodules | 3 +++ src/wasc-worker | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 src/wasc-worker diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d18a868 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/wasc-worker"] + path = src/wasc-worker + url = https://github.com/hexxone/wasc-worker.git diff --git a/src/wasc-worker b/src/wasc-worker new file mode 160000 index 0000000..09e3ff8 --- /dev/null +++ b/src/wasc-worker @@ -0,0 +1 @@ +Subproject commit 09e3ff80682f7217c01d7c4d229aa8ab5fcf3b1f From 6bdfd7651e31632e150a23d222bf14b70afadfb6 Mon Sep 17 00:00:00 2001 From: hexxone Date: Sun, 20 Dec 2020 11:06:26 +0100 Subject: [PATCH 14/76] Rename wasm to wasc --- src/{wasm/WasmPlugin.js => wasc/WascPlugin.js} | 4 ++-- src/{wasm => wasc}/asconfig.json | 0 src/{wasm => wasc}/assembly/index.ts | 0 src/{wasm => wasc}/assembly/tsconfig.json | 0 src/{wasm => wasc}/build/optimized.wat | 0 src/{wasm => wasc}/build/untouched.wat | 0 src/{wasm => wasc}/index.js | 0 src/{wasm => wasc}/package-lock.json | 0 src/{wasm => wasc}/package.json | 0 src/{wasm => wasc}/tests/index.js | 0 src/weas/WEAS.ts | 9 +-------- 11 files changed, 3 insertions(+), 10 deletions(-) rename src/{wasm/WasmPlugin.js => wasc/WascPlugin.js} (98%) rename src/{wasm => wasc}/asconfig.json (100%) rename src/{wasm => wasc}/assembly/index.ts (100%) rename src/{wasm => wasc}/assembly/tsconfig.json (100%) rename src/{wasm => wasc}/build/optimized.wat (100%) rename src/{wasm => wasc}/build/untouched.wat (100%) rename src/{wasm => wasc}/index.js (100%) rename src/{wasm => wasc}/package-lock.json (100%) rename src/{wasm => wasc}/package.json (100%) rename src/{wasm => wasc}/tests/index.js (100%) diff --git a/src/wasm/WasmPlugin.js b/src/wasc/WascPlugin.js similarity index 98% rename from src/wasm/WasmPlugin.js rename to src/wasc/WascPlugin.js index 528de04..9806e6b 100644 --- a/src/wasm/WasmPlugin.js +++ b/src/wasc/WascPlugin.js @@ -78,7 +78,7 @@ function compileWasm(inputPath) { } // actual webpack plugin -class WasmPlugin { +class WascPlugin { options = {}; @@ -124,4 +124,4 @@ class WasmPlugin { } } -module.exports = WasmPlugin; \ No newline at end of file +module.exports = WascPlugin; \ No newline at end of file diff --git a/src/wasm/asconfig.json b/src/wasc/asconfig.json similarity index 100% rename from src/wasm/asconfig.json rename to src/wasc/asconfig.json diff --git a/src/wasm/assembly/index.ts b/src/wasc/assembly/index.ts similarity index 100% rename from src/wasm/assembly/index.ts rename to src/wasc/assembly/index.ts diff --git a/src/wasm/assembly/tsconfig.json b/src/wasc/assembly/tsconfig.json similarity index 100% rename from src/wasm/assembly/tsconfig.json rename to src/wasc/assembly/tsconfig.json diff --git a/src/wasm/build/optimized.wat b/src/wasc/build/optimized.wat similarity index 100% rename from src/wasm/build/optimized.wat rename to src/wasc/build/optimized.wat diff --git a/src/wasm/build/untouched.wat b/src/wasc/build/untouched.wat similarity index 100% rename from src/wasm/build/untouched.wat rename to src/wasc/build/untouched.wat diff --git a/src/wasm/index.js b/src/wasc/index.js similarity index 100% rename from src/wasm/index.js rename to src/wasc/index.js diff --git a/src/wasm/package-lock.json b/src/wasc/package-lock.json similarity index 100% rename from src/wasm/package-lock.json rename to src/wasc/package-lock.json diff --git a/src/wasm/package.json b/src/wasc/package.json similarity index 100% rename from src/wasm/package.json rename to src/wasc/package.json diff --git a/src/wasm/tests/index.js b/src/wasc/tests/index.js similarity index 100% rename from src/wasm/tests/index.js rename to src/wasc/tests/index.js diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index e34f3f0..1cc892d 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -119,14 +119,7 @@ export class WEAS extends CComponent { }); }); - - }) - .catch(ex => { - // ex is a string that represents the exception - Smallog.Error("weas error: [" + ex.filename + ", Line: " + ex.lineno + "] " + ex.message); - }); - - + }).catch(ex => Smallog.Error(JSON.stringify(ex))); } public hasAudio() { From 7c2aef009b140a67b3437b4ee5604554e9cf1c82 Mon Sep 17 00:00:00 2001 From: hexxone Date: Sat, 26 Dec 2020 00:55:44 +0100 Subject: [PATCH 15/76] Lots of WASM changes --- src/WEICUE.ts | 2 +- src/WEWWA.ts | 50 ++- src/WarnHelper.ts | 535 +------------------------- src/wasc-worker | 2 +- src/wasc/WascPlugin.js | 44 ++- src/wasc/assembly/index.asc | 252 +++++++++++++ src/wasc/assembly/index.ts | 166 ++++---- src/wasc/build/optimized.wat | 710 +++++++++++++++++++---------------- src/wasc/build/untouched.wat | 596 +++++++++++++++++------------ src/wasc/index.js | 5 +- src/wasc/package.json | 4 +- src/weas/WEAS.ts | 174 +++++++-- src/weas/WEAS.wasm.asc | 166 ++++---- src/worker-loader.d.ts | 2 +- 14 files changed, 1393 insertions(+), 1315 deletions(-) create mode 100644 src/wasc/assembly/index.asc diff --git a/src/WEICUE.ts b/src/WEICUE.ts index fa126f9..60fc163 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -367,7 +367,7 @@ AAAASUVORK5CYII= document.body.removeChild(this.preview); this.preview = null; } - else Object.apply(this.preview.style, this.getArea(true)); + else Object.assign(this.preview.style, this.getArea(true)); } } diff --git a/src/WEWWA.ts b/src/WEWWA.ts index a436f2d..b02ac77 100644 --- a/src/WEWWA.ts +++ b/src/WEWWA.ts @@ -83,12 +83,8 @@ export class WEWWA { if(CC) {} const cc = window['cookieconsent'].initialise({ palette: { - popup: { - background: "#000" - }, - button: { - background: "#f1d600" - } + popup: { background: "#000" }, + button: { background: "#f1d600" } }, position: "bottom-left", theme: "edgeless" @@ -132,6 +128,10 @@ export class WEWWA { var last = localStorage.getItem("wewwaLastProps"); if (last != null) { var merged = Object.assign(props, JSON.parse(last)); + merged.audioprocessing = { + value: this.project.general.supportsaudioprocessing, + type: "hidden" + }; this.project.general.properties = merged; Smallog.Debug("Loaded & merged settings.", LogHead) } @@ -183,6 +183,18 @@ export class WEWWA { .wewwaMenu a:hover { background: #4CAF50; } + .wewwaMenu .red { + border-color: #FF7F50; + } + .wewwaMenu .red:hover { + background-color: #FF7F50; + } + .wewwaMenu .orange { + border-color: #FFA500; + } + .wewwaMenu .orange:hover { + background-color: #FFA500; + } .wewwaMenu audio { width: 100%; } @@ -275,18 +287,23 @@ export class WEWWA { var td1 = ce("td"); td1.innerHTML = "

Audio Input


"; td1.setAttribute("colspan", 3); + var aBtn1 = ce("a"); - var aBtn2 = ce("a"); - var aBtn3 = ce("input"); + aBtn1.classList.add("orange"); aBtn1.innerHTML = "Microphone"; aBtn1.addEventListener("click", e => { self.requestMicrophone(); }); + + var aBtn2 = ce("a"); + aBtn2.classList.add("orange"); aBtn2.innerHTML = "Select URL"; aBtn2.addEventListener("click", e => { var uri = prompt("Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!", "https://example.com/test.mp3"); self.initiateAudio(uri); }); + + var aBtn3 = ce("input"); aBtn3.id = "wewwaAudioInput"; aBtn3.innerHTML = "Select File"; aBtn3.setAttribute("type", "file"); @@ -295,6 +312,7 @@ export class WEWWA { if (!file) return; self.initiateAudio(file); }); + td1.append(aBtn1, aBtn2, aBtn3); row.append(td1); @@ -305,7 +323,7 @@ export class WEWWA { dropt1.setAttribute("colspan", 3); var dropArea = ce("div"); dropArea.innerHTML = "Drag & Drop" - dropArea.classList.add("droparea"); + dropArea.classList.add(...["droparea", "red"]); dropArea.addEventListener('dragover', (evt) => { evt.stopPropagation(); evt.preventDefault(); @@ -325,6 +343,7 @@ export class WEWWA { var hrtd1 = ce("td"); var hrtd2 = ce("td"); var hrstop = ce("a"); + hrstop.classList.add("red"); hrstop.innerHTML = "Stop All Audio"; hrstop.addEventListener("click", e => { self.stopAudioInterval(); @@ -410,6 +429,7 @@ export class WEWWA { var preFoot = ce("div"); preFoot.innerHTML = "

"; var reset = ce("a"); + reset.classList.add("red") reset.innerHTML = "Reset Settings"; reset.addEventListener("click", e => { if(!window.confirm("This action will clear ALL local data!\r\n\r\nAre you sure?")) return; @@ -422,9 +442,9 @@ export class WEWWA { // footer with ident var footer = ce("div"); - footer.innerHTML = "

[W] allpaper
[E] ngine
[W] eb
[W] allpaper
[A] dapterby hexxone"; + footer.innerHTML = "

[W] allpaper
[E] ngine
[W] eb
[W] allpaper
[A] dapterby hexxone"; // finish up menu - menu.append(preview, header, link, tmain, preFoot, footer) + menu.append(preview, header, link, tmain, preFoot, footer); // create icon for opening & closing the menu var icon = ce("div"); @@ -444,8 +464,8 @@ export class WEWWA { } private CreateItem(prop, itm) { + if(!itm.type || itm.type == "hidden") return; var self = this; - var ce = (e) => document.createElement(e); var row = ce("tr"); row.setAttribute("id", "wewwa_" + prop); @@ -660,7 +680,11 @@ export class WEWWA { else $("#wewwa_" + p).fadeOut(); // get input dom element - var elm: any = document.getElementById("wewwa_" + p).childNodes[1].childNodes[0]; + + var elm: any = document.getElementById("wewwa_" + p); + if(!elm || elm.childNodes.length < 2) continue; + + elm = elm.childNodes[1].childNodes[0]; switch (prop.type) { case "color": elm.value = this.rgbToHex(prop.value); diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index f260efa..0535c0a 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -47,538 +47,11 @@ export class WarnHelper { } private injectHTML() { - var outer = document.createElement("img"); + var outer = document.createElement("div"); outer.id = "triggerwarn"; - outer.setAttribute("src", ` - data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAACXBIWXMAAC4jAAAuIwF4pT92AAAgAElEQVR42uzdeXxdV3kv/N+z1h7OP -GiWrFmeB1me7dhxnLEkIZCRDCSEoe1bejsAvS2l7Xtf3r4tLfe2ty/QXmjhlgJJSBiSQKAEaCg0TCEhEJoQEkhIQuI4nmXLGs7Ze6/7xz5HOjpny7ZkyZbi3/fz0cfW -3jqD1tlaaz9reBZARERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER -ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE -RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER -ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE -RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER -ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERHNLWAS00PwLttqNq3IDyXT8LbbjDBSD4LnDhdHPPNuW+t5TzZkjIyMjhU9+8pM+S4qIiB -ZoO6eyfcmObH3qFsd1L4JgZHBs7L7dKfWVHy9u3jtcGCsMDg56d999t2Fp0UKkWQS0kNyP7ZJYm++rW9Tw7u7ezqs7Otq68vW5xWKpvhGYw0cyiSFxneLy5cu9n/zkJ -wFLjIiIFprfXLE6l27N/1Znb8dvdHW3L2toqu92Ys7yMT8IDqfcg0Ei5lmWVezs7AyeeuopFhgxACGaS9fmuzP57oY3LGpveWt9Jltnaa0dy3Icy2oaOjaSO+CqY4W4 -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 -di3qNQBUtnMmCAJ/bGws+MQnPsF2joiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIi -IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI -iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIvqGMJ -AAAIABJREFUiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiICDDGbDSzq6Xiub9d -cXzHKb7Pf6t4rl385E663B6vKLfl0z1PRHSm6+SztR5ju8e/I6IyxSIgIqJp3GCcb4z5MN87P3MiopmyXoW/01EA3zrO+RSADRXfPwggOM7PF3iZEBFvQk0OwD8CeAO -A7/C98zMnImIAUiIiTwHYdZwKdQDAjyoOXSIio2fgfV7Ey4+IFpDFpRvRV+V7Z5089585y/jVj58xnSxOwSIiIiIiIgYgRERERETEAISIiIiIiIgByJlijLnWGPMFY8 -xuY8yYMeZFY8zXjDFvMsbo4zzupFLVGWNeY4z5pDHmF8aYYWPMSMVrvNsY0zjD9/0fFa9/9XF+7k0VP3fYGKOO87PfqvjZ7il+5jxjzAeMMY8aY/YYYwrGmCPGmOeMM -Z83xrzVGOPM4edVTqU8WvreNsb8rjHmB8aYQ8aYUWPM08aYjxhjVk/jeU/p95rD99VkjPm/jTHfMca8Unpfe4wx3zDGvMMYkzzB4x8pva8XS9+/1hjz/dK1+JIx5h5j -zAUzKP/9pe+1MebNxph/L72/4dK1/lFjzIqqxy4zxvyDMebnpb+DQ8aYB40xv3686/JUPyNjzEUV1/U+Y4x1Eq/1RxWP+Yt5co32GWPeb4x52BhzsPT7v1wq+z8sLTi -OetxvGWMMgIcrDm+v+P2+PUfXXrkcDpe+F2PMDcaYL/8f9s47zK6q6sPvnkloAYHQIl2KIoLSRJQeLDSRJl2kCggWFBQhE4ZMwicI+uFnAZQSBAQE6TU0KYKAdFDASq -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 -ieJWgnmOPkWSBJinVcC7eVcX7fshw74jiGYKK/r/22v8AxEeMoUqzlRjEB42Ffqd7ARrNJg8b33aGGxpMdIwdlCT/vMMk/cF5pI6qiOPa+oi9bAydhTPHp7GN/Tru5F -n7nEnyb9xiqkE5cM3cRz8TDC37d+QSzyF+NsaVrIVFGOnuPYF6Lx3zYgLGGf7an5um8BiqKq1rmxR9gWIsIFYyKIiIiEmYJmohnJsW71LVkHowXxZWISIZ+ARERERER -ETVAREREREREDRARERERERE1QERERERERA0QERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE -RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER -ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE -RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER -ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERE -RERERERERERERERERERERERERERERERERERERERERERERkQZmZteZ2fVFljnNonvJrzPOv18mwjHc4pddQTHSEGmiU3yY2X3+fa9c73+kYdTPh8EtSsc1D+urfLgWK6 -f28cu9bmaLKORKCuOGy9NdlcdC165+XRFuuc7RzIaa2aDuUhZ287ywUFh397LdzEaZ2VNmFlNpV10K0PIT5e7AQGB4kUXfAx7J+pvjv3sy6/PnFLIiUkUnADOBA8zsV -3nKsl7AWYABRzjnvlewyQ/kOj0IuBDoo9BQWJfpDGA14GjFsjRCRutpZh+Y2fllrv9SoV84SvkFRBo+regXkIXD5Ad1168bhPcwH94vm1k8x/cJ//1FCq3K8/iPOY91 -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 -re7WZ57Du/zRf96IH3+vXfz5rDIjvcIpfxBcIj+xw7hlR/28ySZta/yPqR03Qp6azEeCrlWpf3+lrgHMu5DkQpB3OGdY44qcl1qIR8V1b5aWZ/0zwg0giNkJfN7I1Gb -IB03Ckxs8m+C8McP1zeMP8zeJTji7S+rzyc7ycVmusLqFFZ478XLCD9cHpn+4vT976QvirX8JNRl61iA8SFCsqfZn3eMcHRZrVogJQYDyua2YU+Hr73w5r+O9ds7FGX -rWIYruiHrp7qL7ZPmNm2fj6VWypNx377V/qLzlzfPWGUmfX220x3zHMROqdNfTx869e7IMccB5GXLTFtlnK8dW+AlJGeBpjZ4z58pvu4XsavG3Um9CjlSDnxXEnjuZT -9LZSna5F3u1Eem+kn3rsvlC4uy1HG5Aq3yNeDKA0Q/9k5/pjMzAZG2EakNF1qOosaTyWWJyU3QMq8DkQtBxcK6zxxUpPrUMT0XG4D5G4/bHgvRLqwAXJwtcfnF5Ga59 -txhRr+5S4rIvJjLzN/4OGwgv9lrE2ponr0DEh5rgE+Ag5XUIiIiIj8YB1MMJDAOQoKNUC6lB8J6e/AQVH6pYqIiIhI92JmfXx9r9U/RC9qgHR5I+QW4DbgFIWGiIiIy -A/O/yMYMvwMBYWIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI -iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiEg -B/x9OcNpH2VDkjgAAAABJRU5ErkJggg== - `); + outer.innerHTML = ` + + `; document.body.append(outer); } diff --git a/src/wasc-worker b/src/wasc-worker index 09e3ff8..8b905f1 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 09e3ff80682f7217c01d7c4d229aa8ab5fcf3b1f +Subproject commit 8b905f11e17ef8aa87d5cd96e85169bab5f24e64 diff --git a/src/wasc/WascPlugin.js b/src/wasc/WascPlugin.js index 9806e6b..3029696 100644 --- a/src/wasc/WascPlugin.js +++ b/src/wasc/WascPlugin.js @@ -22,12 +22,17 @@ const { execSync } = require('child_process'); const pluginName = 'WasmPlugin'; const sourcePath = path.resolve(__dirname, 'assembly/index.ts'); -const targetPath = path.resolve(__dirname, 'build/optimized.wasm'); +const optPath = path.resolve(__dirname, 'build/optimized.wasm'); +const untPath = path.resolve(__dirname, 'build/untouched.wasm'); +const untMap = path.resolve(__dirname, 'build/untouched.wasm.map'); // schema for options object const schema = { type: 'object', properties: { + production: { + typew: 'bool' + }, relpath: { type: 'string' }, @@ -54,8 +59,8 @@ function getAllFiles(baseDir, subDir, arrayOfFiles) { // compile assemblyscript (typescript) module to wasm and return binary -function compileWasm(inputPath) { - return new Promise((resolve) => { +function compileWasm(inputPath, production) { + return new Promise(resolve => { // copy .wasm.ts -> index.ts // File destination.txt will be created or overwritten by default. fs.copyFile(inputPath, sourcePath, (err) => { @@ -65,10 +70,17 @@ function compileWasm(inputPath) { let output = execSync('npm run asbuild', { cwd: __dirname }); console.log("Compile result: " + output); // none? -> read and resolve optimized.wasm string - var binary = fs.readFileSync(targetPath); - resolve(binary); + if (production) + resolve({ + normal: fs.readFileSync(optPath) + }); + else + resolve({ + normal: fs.readFileSync(untPath), + map: fs.readFileSync(untMap) + }); } - catch(ex) { + catch (ex) { console.warn(pluginName + " Compile Error!"); console.error(ex); } @@ -98,23 +110,27 @@ class WascPlugin { }, async () => { if (addedOnce) return; addedOnce = true; - console.log('This is an experimental plugin!'); // add static files from folder const rPath = path.resolve(__dirname, this.options.relpath); var sFiles = getAllFiles(rPath, ""); + for (var staticFile in sFiles) { + const sFile = sFiles[staticFile]; const sName = sFile.replace(/^.*[\\\/]/, ''); - // @TODO if regex match wasm name, compile + + // if regex match wasm name, compile if (sName.match(this.options.regexx)) { - console.info("Compile wasm: " + sName); - const cWasm = await compileWasm(rPath + sFile); - // @todo keep ".wasm" and remove ".ts" part of name - const newName = sName.replace(/\.[^/.]+$/, ""); - console.info("Success! File: " + newName); - compilation.emitAsset(newName, new RawSource(cWasm)); + console.info(`Compile ${this.options.production ? "prod" : "debug"} wasm: ${sName}`); + compileWasm(rPath + sFile, this.options.production).then(res => { + // keep ".wasm" and remove ".ts" part of name + const newName = sName.replace(/\.[^/.]+$/, ""); + console.info("Success! File: " + newName); + if (res.normal) compilation.emitAsset(newName, new RawSource(res.normal)); + if (res.map) compilation.emitAsset(newName + ".map", new RawSource(res.map)); + }); } } diff --git a/src/wasc/assembly/index.asc b/src/wasc/assembly/index.asc new file mode 100644 index 0000000..dc25421 --- /dev/null +++ b/src/wasc/assembly/index.asc @@ -0,0 +1,252 @@ +/** + * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Wallaper Engine Audio Supplier worker. + */ + +const DAT_LEN = 128; +const HLF_LEN = 64; +const QRT_LEN = 32; + +const pinkNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, + 0.5452348949654, 0.50723325864167, 0.4677726234682, 0.44204182748767, 0.41956517802157, + 0.41517375040002, 0.41312118577934, 0.40618363960446, 0.39913707474975, 0.38207008614508, + 0.38329789106488, 0.37472136606245, 0.36586428412968, 0.37603017335105, 0.39762590761573, + 0.39391828858591, 0.37930603769622, 0.39433365764563, 0.38511504613859, 0.39082579241834, + 0.3811852720504, 0.40231453727161, 0.40244151133175, 0.39965366884521, 0.39761103827545, + 0.51136400422212, 0.66151212038954, 0.66312205226679, 0.7416276690995, 0.74614971301133, + 0.84797007577483, 0.8573583910469, 0.96382997811663, 0.99819377577185, 1.0628692615814, + 1.1059083969751, 1.1819808497335, 1.257092297208, 1.3226521464753, 1.3735992532905, + 1.4953223705889, 1.5310064942373, 1.6193923584808, 1.7094805527135, 1.7706604552218, + 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, + 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, + 2.4043566176474, 2.4280476777842, 2.3917477397336, 2.4032522546622, 2.3614180150678]; + +// correct pink noise for first and second half +function correctPinkNoise(data: Float64Array): void { + var correct = new Float64Array(DAT_LEN); + for (var i = 0; i < HLF_LEN; i++) { + correct[i] = data[i] / pinkNoise[i]; + correct[HLF_LEN + i] = data[HLF_LEN + i] / pinkNoise[i]; + } + data.set(correct); +} + +// merge first and second half into full range +function stereoToMono(data: Float64Array): void { + var mono = new Float64Array(DAT_LEN); + var mIdx = 0; + for (var i = 0; i < HLF_LEN; i++) { + mono[mIdx++] = data[i]; + mono[mIdx++] = data[HLF_LEN + i]; + } + data.set(mono); +} + +// switch front with back in first half +function invertFirst(data: Float64Array): void { + for (var i = 0; i < QRT_LEN; i++) { + var a = data[i]; + data[i] = data[HLF_LEN - i]; + data[HLF_LEN - i] = a; + } +} + +// switch front with back in second half +function invertSecond(data: Float64Array): void { + for (var i = 0; i < QRT_LEN; i++) { + var b = data[HLF_LEN + i]; + data[HLF_LEN + i] = data[DAT_LEN - i]; + data[DAT_LEN - i] = b; + } +} + +// switch front with back in full range +function invertAll(data: Float64Array): void { + for (var i = 0; i < HLF_LEN; i++) { + var a = data[i]; + data[i] = data[DAT_LEN - i]; + data[DAT_LEN - i] = a; + } +} + +// filter peaks for full range +function peakFilter(array: Float64Array, amount: f64): void { + var oldMax: f64 = 0; + var newMax: f64 = 0; + var newArray = new Float64Array(DAT_LEN); + var i = 0; + // pow this shit + for (; i < DAT_LEN; i++) { + if (array[i] > oldMax) oldMax = array[i]; + newArray[i] = Math.pow(array[i] * amount, amount) as f64; + if (newArray[i] > newMax) newMax = newArray[i]; + } + // re-scale & apply + var divide: f64 = newMax / oldMax; + for (i = 0; i < DAT_LEN; i++) + array[i] = newArray[i] / divide; +} + +// smooth values for full range +function smoothArray(array: Float64Array, steps: i32): void { + var newArray = new Float64Array(DAT_LEN); + // make smoothed array + for (var outer = 0; outer < DAT_LEN; outer++) { + var sum: f64 = 0; + for (var inner = outer - steps; inner <= outer + steps; inner++) { + var idx = inner; + if (idx < 0) idx += DAT_LEN; + sum += array[idx % DAT_LEN]; + } + newArray[outer] = sum / (((steps * 2) + 1) as f64); + } + array.set(newArray); +} + +// function will apply setting-defined data smoothing +function applyValueLeveling(curr: Float64Array, prev: Float64Array, inc: f64, dec: f64): void { + for (var i = 0; i < DAT_LEN; i++) { + var diff: f64 = curr[i] - prev[i]; + var mlt: f64 = 100 - (diff > 0 ? inc : dec); + curr[i] -= diff * mlt / 100; + } +} + +// this will hold the current processed audio data +// either: B-H | H-B | HL-BL-BR-HR | BL-HL-HR-BR +// where ( B=bass, H=high, L=left, R=right ) +// in range > 0.0 and ~< 1.5 +export const audioData = new Float64Array(DAT_LEN); + +// this will hold the current processed properties: +export const audioProps = new Float64Array(10); +enum Props { + bass = 0, + mids = 1, + highs = 2, + sum = 3, + min = 4, + max = 5, + average = 6, + range = 7, + silent = 8, + intensity = 9 +} + + +// this will hold the current processing settings +export const audioSettings = new Float64Array(8); +enum Sett { + equalize = 0, + mono_audio = 1, + audio_direction = 2, + peak_filter = 3, + value_smoothing = 4, + audio_increase = 5, + audio_decrease = 6, + minimum_volume = 7 +} + +// check helper +function isOn(a: f64): boolean { + return a > 0; +} + +// this will update and process new data +export function update(newData: Float64Array): void { + + // fix pink noise? + if (isOn(audioSettings[Sett.equalize])) + correctPinkNoise(newData); + + // write botch channels to mono + if (isOn(audioSettings[Sett.mono_audio])) + stereoToMono(newData); + + if (isOn(audioSettings[Sett.audio_direction])) { + // flipped high & low mapping + if (isOn(audioSettings[Sett.mono_audio])) + // flip whole range + invertAll(newData); + else { + // only flip first half of stereo + invertFirst(newData); + } + + } + else { + // normal high & low mapping + if (isOn(audioSettings[Sett.mono_audio])) { + // only flip the second half of the data + invertSecond(newData); + } + } + + // process peaks? + if (isOn(audioSettings[Sett.peak_filter])) + peakFilter(newData, audioSettings[Sett.peak_filter] + 1); + + // smooth data? + if (isOn(audioSettings[Sett.value_smoothing])) + smoothArray(newData, audioSettings[Sett.value_smoothing] as i32); + + // process with last data? + if (audioData) + applyValueLeveling(newData, audioData, + audioSettings[Sett.audio_increase], + audioSettings[Sett.audio_decrease]); + + // process current frequency data and previous if given + var sum: f64 = 0, + min: f64 = 1, + max: f64 = 0, + bass: f64 = 0, + mids: f64 = 0, + peaks: f64 = 0; + + for (var indx = 0; indx < DAT_LEN; indx++) { + // parse current freq value + var idata: f64 = newData[indx]; + // process min max value + if (idata < min) min = idata; + if (idata > max) max = idata; + // process ranges + if (indx < (42 / 3)) bass += idata * audioProps[Props.bass]; + else if (indx > 69) peaks += idata * audioProps[Props.highs]; + else mids += idata * audioProps[Props.mids]; + // calc peak average + sum += idata; + } + + // calc average with previous entry + var average: f64 = sum / (DAT_LEN as f64); + var silent: f64 = (max < audioSettings[Sett.minimum_volume] / 1000) ? 0.9999 : 0.00; + var intensity: f64 = (bass * 8 - mids + peaks) / 6 / average; + var range: f64 = max - min; + + // Apply Data + audioData.set(newData); + audioProps.set([ + bass, + mids, + peaks, + sum, + min, + max, + average, + range, + silent, + intensity + ]); + + // clean + gc.collect(); +} + diff --git a/src/wasc/assembly/index.ts b/src/wasc/assembly/index.ts index dc25421..fc748e6 100644 --- a/src/wasc/assembly/index.ts +++ b/src/wasc/assembly/index.ts @@ -10,11 +10,46 @@ * Wallaper Engine Audio Supplier worker. */ -const DAT_LEN = 128; -const HLF_LEN = 64; -const QRT_LEN = 32; -const pinkNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, + ////////////////////////// + // CUSTOM API + ////////////////////////// + +@external("env", "logf") +declare function logf(value: f64): void; + +@external("env", "logi") +declare function logi(value: u32): void; + +@external("env", "logU32Array") +declare function logU32Array(arr: Uint32Array): void; + +@external("env", "logF64Array") +declare function logF64Array(arr: Float64Array): void; + +export function allocF64Array(length: i32): Float64Array { + return new Float64Array(length); +} + +export function allocU32Array(length: i32): Uint32Array { + return new Uint32Array(length); +} + +@inline +function deallocArray(arr: T[]): void { + memory.free(changetype(arr.buffer_)); + memory.free(changetype(arr)); +} + + ////////////////////////// + // Main Program + ////////////////////////// + +const DAT_LEN: i32 = 128; +const HLF_LEN: i32 = 64; +const QRT_LEN: i32 = 32; + +const pNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, 0.5452348949654, 0.50723325864167, 0.4677726234682, 0.44204182748767, 0.41956517802157, 0.41517375040002, 0.41312118577934, 0.40618363960446, 0.39913707474975, 0.38207008614508, 0.38329789106488, 0.37472136606245, 0.36586428412968, 0.37603017335105, 0.39762590761573, @@ -27,6 +62,9 @@ const pinkNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.637679 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, 2.4043566176474, 2.4280476777842, 2.3917477397336, 2.4032522546622, 2.3614180150678]; +const pinkNoise = new Float64Array(HLF_LEN); +pinkNoise.set(pNoise); + // correct pink noise for first and second half function correctPinkNoise(data: Float64Array): void { @@ -53,8 +91,8 @@ function stereoToMono(data: Float64Array): void { function invertFirst(data: Float64Array): void { for (var i = 0; i < QRT_LEN; i++) { var a = data[i]; - data[i] = data[HLF_LEN - i]; - data[HLF_LEN - i] = a; + data[i] = data[HLF_LEN - 1 - i]; + data[HLF_LEN - 1 - i] = a; } } @@ -62,8 +100,8 @@ function invertFirst(data: Float64Array): void { function invertSecond(data: Float64Array): void { for (var i = 0; i < QRT_LEN; i++) { var b = data[HLF_LEN + i]; - data[HLF_LEN + i] = data[DAT_LEN - i]; - data[DAT_LEN - i] = b; + data[HLF_LEN + i] = data[DAT_LEN - 1 - i]; + data[DAT_LEN - 1 - i] = b; } } @@ -71,8 +109,8 @@ function invertSecond(data: Float64Array): void { function invertAll(data: Float64Array): void { for (var i = 0; i < HLF_LEN; i++) { var a = data[i]; - data[i] = data[DAT_LEN - i]; - data[DAT_LEN - i] = a; + data[i] = data[DAT_LEN - 1 - i]; + data[DAT_LEN - 1 - i] = a; } } @@ -119,40 +157,25 @@ function applyValueLeveling(curr: Float64Array, prev: Float64Array, inc: f64, de } } +// THEW INPUT VALUE TO WRITE TO +export const inputData = new Float64Array(DAT_LEN); +inputData.fill(0.0); + // this will hold the current processed audio data // either: B-H | H-B | HL-BL-BR-HR | BL-HL-HR-BR // where ( B=bass, H=high, L=left, R=right ) // in range > 0.0 and ~< 1.5 -export const audioData = new Float64Array(DAT_LEN); +export const outputData = new Float64Array(DAT_LEN); +outputData.fill(0.0); // this will hold the current processed properties: export const audioProps = new Float64Array(10); -enum Props { - bass = 0, - mids = 1, - highs = 2, - sum = 3, - min = 4, - max = 5, - average = 6, - range = 7, - silent = 8, - intensity = 9 -} - +audioProps.fill(0.0); // this will hold the current processing settings -export const audioSettings = new Float64Array(8); -enum Sett { - equalize = 0, - mono_audio = 1, - audio_direction = 2, - peak_filter = 3, - value_smoothing = 4, - audio_increase = 5, - audio_decrease = 6, - minimum_volume = 7 -} +export const audioSettings = new Float64Array(11); +audioSettings.fill(0.0); + // check helper function isOn(a: f64): boolean { @@ -160,48 +183,49 @@ function isOn(a: f64): boolean { } // this will update and process new data -export function update(newData: Float64Array): void { +export function update(): void { // fix pink noise? - if (isOn(audioSettings[Sett.equalize])) - correctPinkNoise(newData); + if (isOn(audioSettings[0])) + correctPinkNoise(inputData); // write botch channels to mono - if (isOn(audioSettings[Sett.mono_audio])) - stereoToMono(newData); + if (isOn(audioSettings[1])) + stereoToMono(inputData); - if (isOn(audioSettings[Sett.audio_direction])) { + if (isOn(audioSettings[2])) { // flipped high & low mapping - if (isOn(audioSettings[Sett.mono_audio])) + if (isOn(audioSettings[1])) // flip whole range - invertAll(newData); + invertAll(inputData); else { // only flip first half of stereo - invertFirst(newData); + invertFirst(inputData); } - } else { // normal high & low mapping - if (isOn(audioSettings[Sett.mono_audio])) { + if (isOn(audioSettings[1])) { // only flip the second half of the data - invertSecond(newData); + invertSecond(inputData); } } // process peaks? - if (isOn(audioSettings[Sett.peak_filter])) - peakFilter(newData, audioSettings[Sett.peak_filter] + 1); + if (isOn(audioSettings[3])) + peakFilter(inputData, audioSettings[3] + 1); // smooth data? - if (isOn(audioSettings[Sett.value_smoothing])) - smoothArray(newData, audioSettings[Sett.value_smoothing] as i32); + if (isOn(audioSettings[4])) + smoothArray(inputData, Math.floor(audioSettings[4]) as i32); - // process with last data? - if (audioData) - applyValueLeveling(newData, audioData, - audioSettings[Sett.audio_increase], - audioSettings[Sett.audio_decrease]); + //logF64Array(inputData); + //logF64Array(outputData); + + // process with last data + applyValueLeveling(inputData, outputData, + audioSettings[5], + audioSettings[6]); // process current frequency data and previous if given var sum: f64 = 0, @@ -213,40 +237,26 @@ export function update(newData: Float64Array): void { for (var indx = 0; indx < DAT_LEN; indx++) { // parse current freq value - var idata: f64 = newData[indx]; + var idata: f64 = inputData[indx]; // process min max value if (idata < min) min = idata; if (idata > max) max = idata; // process ranges - if (indx < (42 / 3)) bass += idata * audioProps[Props.bass]; - else if (indx > 69) peaks += idata * audioProps[Props.highs]; - else mids += idata * audioProps[Props.mids]; + if (indx < (42 / 3)) bass += idata * audioSettings[9]; + else if (indx > 69) peaks += idata * audioSettings[7]; + else mids += idata * audioSettings[8]; // calc peak average sum += idata; } // calc average with previous entry var average: f64 = sum / (DAT_LEN as f64); - var silent: f64 = (max < audioSettings[Sett.minimum_volume] / 1000) ? 0.9999 : 0.00; + var silent: f64 = (max < audioSettings[10] / 1000) ? 0.9999 : 0.00; var intensity: f64 = (bass * 8 - mids + peaks) / 6 / average; var range: f64 = max - min; // Apply Data - audioData.set(newData); - audioProps.set([ - bass, - mids, - peaks, - sum, - min, - max, - average, - range, - silent, - intensity - ]); - - // clean - gc.collect(); + outputData.set(inputData); + audioProps.set([bass,mids,peaks,sum,min,max,average,range,silent,intensity]) + // DONE } - diff --git a/src/wasc/build/optimized.wat b/src/wasc/build/optimized.wat index 44281e9..427b57e 100644 --- a/src/wasc/build/optimized.wat +++ b/src/wasc/build/optimized.wat @@ -1,14 +1,13 @@ (module - (type $i32_i32_=>_none (func (param i32 i32))) - (type $i32_=>_none (func (param i32))) (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_i32_=>_none (func (param i32 i32))) (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) (type $none_=>_none (func)) + (type $i32_=>_none (func (param i32))) (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) + (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) (type $i32_i32_f64_=>_none (func (param i32 i32 f64))) - (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) - (type $i32_=>_f64 (func (param i32) (result f64))) (type $i32_i32_=>_f64 (func (param i32 i32) (result f64))) (type $f64_f64_=>_f64 (func (param f64 f64) (result f64))) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) @@ -23,21 +22,24 @@ (data (i32.const 1868) "&\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00&\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00b\00u\00f\00f\00e\00r\00.\00t\00s") (data (i32.const 1932) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e") (data (i32.const 1996) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00~\00l\00i\00b\00/\00t\00y\00p\00e\00d\00a\00r\00r\00a\00y\00.\00t\00s") - (data (i32.const 2060) "\1a\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s") - (data (i32.const 2112) "\07\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\"\1a\00\00\00\00\00\00!\1a\00\00\02\00\00\00a\00\00\00\02\00\00\00\"\t") + (data (i32.const 2064) "\08\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\"\1a\00\00\00\00\00\00!\1a\00\00\02\00\00\00a\00\00\00\02\00\00\00!\01\00\00\02\00\00\00\"\t") (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) - (global $assembly/index/audioData (mut i32) (i32.const 0)) + (global $assembly/index/pinkNoise (mut i32) (i32.const 0)) + (global $assembly/index/inputData (mut i32) (i32.const 0)) + (global $assembly/index/outputData (mut i32) (i32.const 0)) (global $assembly/index/audioProps (mut i32) (i32.const 0)) (global $assembly/index/audioSettings (mut i32) (i32.const 0)) - (global $~lib/rt/__rtti_base i32 (i32.const 2112)) + (global $~lib/rt/__rtti_base i32 (i32.const 2064)) (export "memory" (memory $0)) (export "__new" (func $~lib/rt/pure/__new)) (export "__renew" (func $~lib/rt/pure/__renew)) (export "__retain" (func $~lib/rt/pure/__retain)) (export "__release" (func $~lib/rt/pure/__release)) - (export "__collect" (func $~lib/rt/pure/__collect)) (export "__rtti_base" (global $~lib/rt/__rtti_base)) - (export "audioData" (global $assembly/index/audioData)) + (export "allocF64Array" (func $assembly/index/allocF64Array)) + (export "allocU32Array" (func $assembly/index/allocU32Array)) + (export "inputData" (global $assembly/index/inputData)) + (export "outputData" (global $assembly/index/outputData)) (export "audioProps" (global $assembly/index/audioProps)) (export "audioSettings" (global $assembly/index/audioSettings)) (export "update" (func $assembly/index/update)) @@ -609,10 +611,10 @@ if unreachable end - i32.const 2176 + i32.const 2144 i32.const 0 i32.store - i32.const 3744 + i32.const 3712 i32.const 0 i32.store loop $for-loop|0 @@ -623,7 +625,7 @@ local.get $1 i32.const 2 i32.shl - i32.const 2176 + i32.const 2144 i32.add i32.const 0 i32.store offset=4 @@ -641,7 +643,7 @@ i32.add i32.const 2 i32.shl - i32.const 2176 + i32.const 2144 i32.add i32.const 0 i32.store offset=96 @@ -659,13 +661,13 @@ br $for-loop|0 end end - i32.const 2176 - i32.const 3748 + i32.const 2144 + i32.const 3716 memory.size i32.const 16 i32.shl call $~lib/rt/tlsf/addMemory - i32.const 2176 + i32.const 2144 global.set $~lib/rt/tlsf/ROOT ) (func $~lib/rt/tlsf/prepareSize (param $0 i32) (result i32) @@ -1287,7 +1289,7 @@ i32.and call $~lib/memory/memory.copy local.get $1 - i32.const 2172 + i32.const 2132 i32.ge_u if local.get $0 @@ -1329,7 +1331,7 @@ i32.add local.set $2 local.get $0 - i32.const 2172 + i32.const 2132 i32.lt_u if global.get $~lib/rt/tlsf/ROOT @@ -1424,7 +1426,7 @@ (local $1 i32) (local $2 i32) local.get $0 - i32.const 2172 + i32.const 2132 i32.gt_u if local.get $0 @@ -1471,7 +1473,7 @@ ) (func $~lib/rt/pure/__release (param $0 i32) local.get $0 - i32.const 2172 + i32.const 2132 i32.gt_u if local.get $0 @@ -1638,35 +1640,31 @@ end end ) - (func $~lib/typedarray/Float64Array#constructor (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) + (func $~lib/arraybuffer/ArrayBufferView#constructor (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) (local $4 i32) - i32.const 12 - i32.const 4 - call $~lib/rt/pure/__new - call $~lib/rt/pure/__retain - local.tee $1 + local.get $0 i32.eqz if i32.const 12 i32.const 2 call $~lib/rt/pure/__new call $~lib/rt/pure/__retain - local.set $1 + local.set $0 end - local.get $1 + local.get $0 i32.const 0 i32.store - local.get $1 + local.get $0 i32.const 0 i32.store offset=4 - local.get $1 + local.get $0 i32.const 0 i32.store offset=8 - local.get $0 - i32.const 134217727 + local.get $1 + i32.const 1073741820 + local.get $2 + i32.shr_u i32.gt_u if i32.const 1840 @@ -1676,19 +1674,19 @@ call $~lib/builtins/abort unreachable end - local.get $0 - i32.const 3 + local.get $1 + local.get $2 i32.shl local.tee $3 i32.const 0 call $~lib/rt/pure/__new - local.tee $0 + local.tee $1 local.get $3 call $~lib/memory/memory.fill - local.get $0 + local.get $1 local.set $2 - local.get $0 local.get $1 + local.get $0 i32.load local.tee $4 i32.ne @@ -1699,56 +1697,135 @@ local.get $4 call $~lib/rt/pure/__release end - local.get $1 + local.get $0 local.get $2 i32.store - local.get $1 local.get $0 - i32.store offset=4 local.get $1 + i32.store offset=4 + local.get $0 local.get $3 i32.store offset=8 - local.get $1 + local.get $0 ) - (func $~lib/typedarray/Float64Array#__get (param $0 i32) (param $1 i32) (result f64) + (func $~lib/typedarray/Float64Array#constructor (param $0 i32) (result i32) + i32.const 12 + i32.const 4 + call $~lib/rt/pure/__new + call $~lib/rt/pure/__retain + local.get $0 + i32.const 3 + call $~lib/arraybuffer/ArrayBufferView#constructor + ) + (func $~lib/typedarray/Float64Array#set<~lib/array/Array> (param $0 i32) (param $1 i32) + (local $2 i32) local.get $1 + call $~lib/rt/pure/__retain + local.set $2 + local.get $0 + call $~lib/rt/pure/__retain + local.set $0 + local.get $2 + call $~lib/rt/pure/__retain + local.tee $1 + i32.load offset=12 local.get $0 i32.load offset=8 i32.const 3 i32.shr_u - i32.ge_u + i32.gt_s if i32.const 1952 i32.const 2016 - i32.const 1304 - i32.const 64 + i32.const 1775 + i32.const 47 call $~lib/builtins/abort unreachable end local.get $0 i32.load offset=4 local.get $1 + i32.load offset=4 + local.get $1 + i32.load offset=8 + call $~lib/memory/memory.copy + local.get $1 + call $~lib/rt/pure/__release + local.get $0 + call $~lib/rt/pure/__release + local.get $2 + call $~lib/rt/pure/__release + ) + (func $~lib/typedarray/Float64Array#fill (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + local.get $0 + call $~lib/rt/pure/__retain + local.tee $1 + i32.load offset=4 + local.set $3 + i32.const 0 + local.get $1 + i32.load offset=8 i32.const 3 - i32.shl - i32.add - f64.load + i32.shr_u + local.tee $2 + local.get $2 + select + local.set $0 + loop $for-loop|0 + local.get $0 + local.get $2 + i32.lt_s + if + local.get $3 + local.get $0 + i32.const 3 + i32.shl + i32.add + f64.const 0 + f64.store + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|0 + end + end + local.get $1 ) - (func $~lib/array/Array#__get (param $0 i32) (result f64) + (func $assembly/index/allocF64Array (param $0 i32) (result i32) local.get $0 - i32.const 1804 - i32.load + call $~lib/typedarray/Float64Array#constructor + ) + (func $assembly/index/allocU32Array (param $0 i32) (result i32) + i32.const 12 + i32.const 6 + call $~lib/rt/pure/__new + call $~lib/rt/pure/__retain + local.get $0 + i32.const 2 + call $~lib/arraybuffer/ArrayBufferView#constructor + ) + (func $~lib/typedarray/Float64Array#__get (param $0 i32) (param $1 i32) (result f64) + local.get $1 + local.get $0 + i32.load offset=8 + i32.const 3 + i32.shr_u i32.ge_u if i32.const 1952 - i32.const 2080 - i32.const 104 - i32.const 42 + i32.const 2016 + i32.const 1304 + i32.const 64 call $~lib/builtins/abort unreachable end - i32.const 1796 - i32.load local.get $0 + i32.load offset=4 + local.get $1 i32.const 3 i32.shl i32.add @@ -2833,9 +2910,10 @@ f64.const 1e-300 f64.mul ) - (func $assembly/index/update (param $0 i32) + (func $assembly/index/update + (local $0 f64) (local $1 f64) - (local $2 f64) + (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) @@ -2845,57 +2923,54 @@ (local $9 f64) (local $10 f64) (local $11 f64) - (local $12 i32) + (local $12 f64) (local $13 f64) (local $14 f64) - (local $15 f64) - local.get $0 - call $~lib/rt/pure/__retain - local.set $6 + (local $15 i32) global.get $assembly/index/audioSettings i32.const 0 call $~lib/typedarray/Float64Array#__get f64.const 0 f64.gt if - i32.const 0 - local.set $0 - local.get $6 + global.get $assembly/index/inputData call $~lib/rt/pure/__retain local.set $4 i32.const 128 call $~lib/typedarray/Float64Array#constructor local.set $5 loop $for-loop|0 - local.get $0 + local.get $3 i32.const 64 i32.lt_s if local.get $5 - local.get $0 + local.get $3 local.get $4 - local.get $0 + local.get $3 + call $~lib/typedarray/Float64Array#__get + global.get $assembly/index/pinkNoise + local.get $3 call $~lib/typedarray/Float64Array#__get - local.get $0 - call $~lib/array/Array#__get f64.div call $~lib/typedarray/Float64Array#__set local.get $5 - local.get $0 + local.get $3 i32.const -64 i32.sub local.tee $7 local.get $4 local.get $7 call $~lib/typedarray/Float64Array#__get - local.get $0 - call $~lib/array/Array#__get + global.get $assembly/index/pinkNoise + local.get $3 + call $~lib/typedarray/Float64Array#__get f64.div call $~lib/typedarray/Float64Array#__set - local.get $0 + local.get $3 i32.const 1 i32.add - local.set $0 + local.set $3 br $for-loop|0 end end @@ -2914,45 +2989,45 @@ f64.gt if i32.const 0 - local.set $0 + local.set $3 i32.const 0 local.set $7 - local.get $6 + global.get $assembly/index/inputData call $~lib/rt/pure/__retain local.set $4 i32.const 128 call $~lib/typedarray/Float64Array#constructor local.set $5 loop $for-loop|01 - local.get $0 + local.get $3 i32.const 64 i32.lt_s if local.get $5 local.get $7 local.get $4 - local.get $0 + local.get $3 call $~lib/typedarray/Float64Array#__get call $~lib/typedarray/Float64Array#__set local.get $7 i32.const 1 i32.add - local.tee $12 + local.tee $15 i32.const 1 i32.add local.set $7 local.get $5 - local.get $12 + local.get $15 local.get $4 - local.get $0 + local.get $3 i32.const -64 i32.sub call $~lib/typedarray/Float64Array#__get call $~lib/typedarray/Float64Array#__set - local.get $0 + local.get $3 i32.const 1 i32.add - local.set $0 + local.set $3 br $for-loop|01 end end @@ -2976,74 +3051,74 @@ f64.const 0 f64.gt if - local.get $6 + global.get $assembly/index/inputData call $~lib/rt/pure/__retain - local.set $0 + local.set $3 loop $for-loop|04 - local.get $3 + local.get $2 i32.const 64 i32.lt_s if - local.get $0 local.get $3 + local.get $2 call $~lib/typedarray/Float64Array#__get - local.set $2 - local.get $0 + local.set $1 local.get $3 - local.get $0 - i32.const 128 + local.get $2 local.get $3 + i32.const 127 + local.get $2 i32.sub local.tee $4 call $~lib/typedarray/Float64Array#__get call $~lib/typedarray/Float64Array#__set - local.get $0 + local.get $3 local.get $4 - local.get $2 + local.get $1 call $~lib/typedarray/Float64Array#__set - local.get $3 + local.get $2 i32.const 1 i32.add - local.set $3 + local.set $2 br $for-loop|04 end end - local.get $0 + local.get $3 call $~lib/rt/pure/__release else - local.get $6 + global.get $assembly/index/inputData call $~lib/rt/pure/__retain - local.set $0 + local.set $3 loop $for-loop|00 - local.get $3 + local.get $2 i32.const 32 i32.lt_s if - local.get $0 local.get $3 + local.get $2 call $~lib/typedarray/Float64Array#__get - local.set $2 - local.get $0 + local.set $1 local.get $3 - local.get $0 - i32.const 64 + local.get $2 local.get $3 + i32.const 63 + local.get $2 i32.sub local.tee $4 call $~lib/typedarray/Float64Array#__get call $~lib/typedarray/Float64Array#__set - local.get $0 + local.get $3 local.get $4 - local.get $2 + local.get $1 call $~lib/typedarray/Float64Array#__set - local.get $3 + local.get $2 i32.const 1 i32.add - local.set $3 + local.set $2 br $for-loop|00 end end - local.get $0 + local.get $3 call $~lib/rt/pure/__release end else @@ -3053,42 +3128,42 @@ f64.const 0 f64.gt if - local.get $6 + global.get $assembly/index/inputData call $~lib/rt/pure/__retain - local.set $0 + local.set $2 loop $for-loop|016 - local.get $3 + local.get $6 i32.const 32 i32.lt_s if - local.get $0 - local.get $3 + local.get $2 + local.get $6 i32.const -64 i32.sub - local.tee $4 + local.tee $3 call $~lib/typedarray/Float64Array#__get - local.set $2 - local.get $0 - local.get $4 - local.get $0 - i32.const 128 + local.set $1 + local.get $2 local.get $3 + local.get $2 + i32.const 127 + local.get $6 i32.sub - local.tee $4 + local.tee $3 call $~lib/typedarray/Float64Array#__get call $~lib/typedarray/Float64Array#__set - local.get $0 - local.get $4 local.get $2 - call $~lib/typedarray/Float64Array#__set local.get $3 + local.get $1 + call $~lib/typedarray/Float64Array#__set + local.get $6 i32.const 1 i32.add - local.set $3 + local.set $6 br $for-loop|016 end end - local.get $0 + local.get $2 call $~lib/rt/pure/__release end end @@ -3098,6 +3173,7 @@ f64.const 0 f64.gt if + global.get $assembly/index/inputData global.get $assembly/index/audioSettings i32.const 3 call $~lib/typedarray/Float64Array#__get @@ -3105,35 +3181,34 @@ f64.add local.set $11 i32.const 0 - local.set $0 - f64.const 0 local.set $2 - local.get $6 + f64.const 0 + local.set $1 call $~lib/rt/pure/__retain local.set $3 i32.const 128 call $~lib/typedarray/Float64Array#constructor local.set $4 loop $for-loop|08 - local.get $0 + local.get $2 i32.const 128 i32.lt_s if local.get $3 - local.get $0 - call $~lib/typedarray/Float64Array#__get local.get $2 + call $~lib/typedarray/Float64Array#__get + local.get $1 f64.gt if local.get $3 - local.get $0 + local.get $2 call $~lib/typedarray/Float64Array#__get - local.set $2 + local.set $1 end local.get $4 - local.get $0 + local.get $2 local.get $3 - local.get $0 + local.get $2 call $~lib/typedarray/Float64Array#__get local.get $11 f64.mul @@ -3141,46 +3216,46 @@ call $~lib/math/NativeMath.pow call $~lib/typedarray/Float64Array#__set local.get $4 - local.get $0 + local.get $2 call $~lib/typedarray/Float64Array#__get - local.get $1 + local.get $0 f64.gt if local.get $4 - local.get $0 + local.get $2 call $~lib/typedarray/Float64Array#__get - local.set $1 + local.set $0 end - local.get $0 + local.get $2 i32.const 1 i32.add - local.set $0 + local.set $2 br $for-loop|08 end end + local.get $0 local.get $1 - local.get $2 f64.div local.set $1 i32.const 0 - local.set $0 + local.set $2 loop $for-loop|1 - local.get $0 + local.get $2 i32.const 128 i32.lt_s if local.get $3 - local.get $0 + local.get $2 local.get $4 - local.get $0 + local.get $2 call $~lib/typedarray/Float64Array#__get local.get $1 f64.div call $~lib/typedarray/Float64Array#__set - local.get $0 + local.get $2 i32.const 1 i32.add - local.set $0 + local.set $2 br $for-loop|1 end end @@ -3195,40 +3270,41 @@ f64.const 0 f64.gt if + global.get $assembly/index/inputData global.get $assembly/index/audioSettings i32.const 4 call $~lib/typedarray/Float64Array#__get + f64.floor i32.trunc_f64_s local.set $4 i32.const 0 - local.set $0 - local.get $6 + local.set $2 call $~lib/rt/pure/__retain local.set $5 i32.const 128 call $~lib/typedarray/Float64Array#constructor - local.set $7 + local.set $6 loop $for-loop|010 - local.get $0 + local.get $2 i32.const 128 i32.lt_s if f64.const 0 - local.set $2 - local.get $0 + local.set $1 + local.get $2 local.get $4 i32.sub local.set $3 - local.get $0 + local.get $2 local.get $4 i32.add - local.set $12 + local.set $7 loop $for-loop|111 local.get $3 - local.get $12 + local.get $7 i32.le_s if - local.get $2 + local.get $1 local.get $5 local.get $3 i32.const 128 @@ -3242,7 +3318,7 @@ i32.rem_s call $~lib/typedarray/Float64Array#__get f64.add - local.set $2 + local.set $1 local.get $3 i32.const 1 i32.add @@ -3250,9 +3326,9 @@ br $for-loop|111 end end - local.get $7 - local.get $0 + local.get $6 local.get $2 + local.get $1 local.get $4 i32.const 1 i32.shl @@ -3261,103 +3337,102 @@ f64.convert_i32_s f64.div call $~lib/typedarray/Float64Array#__set - local.get $0 + local.get $2 i32.const 1 i32.add - local.set $0 + local.set $2 br $for-loop|010 end end local.get $5 - local.get $7 + local.get $6 call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> local.get $5 call $~lib/rt/pure/__release - local.get $7 + local.get $6 call $~lib/rt/pure/__release end - global.get $assembly/index/audioData - if - global.get $assembly/index/audioData - global.get $assembly/index/audioSettings - i32.const 5 - call $~lib/typedarray/Float64Array#__get - local.set $2 - global.get $assembly/index/audioSettings - i32.const 6 - call $~lib/typedarray/Float64Array#__get - local.set $11 - i32.const 0 - local.set $0 - local.get $6 - call $~lib/rt/pure/__retain - local.set $3 - call $~lib/rt/pure/__retain - local.set $4 - loop $for-loop|012 + global.get $assembly/index/inputData + global.get $assembly/index/outputData + local.set $4 + global.get $assembly/index/audioSettings + i32.const 5 + call $~lib/typedarray/Float64Array#__get + local.set $0 + global.get $assembly/index/audioSettings + i32.const 6 + call $~lib/typedarray/Float64Array#__get + local.set $11 + i32.const 0 + local.set $2 + call $~lib/rt/pure/__retain + local.set $3 + local.get $4 + call $~lib/rt/pure/__retain + local.set $4 + loop $for-loop|012 + local.get $2 + i32.const 128 + i32.lt_s + if + local.get $3 + local.get $2 + call $~lib/typedarray/Float64Array#__get + local.get $4 + local.get $2 + call $~lib/typedarray/Float64Array#__get + f64.sub + local.set $1 + local.get $3 + local.get $2 + local.get $3 + local.get $2 + call $~lib/typedarray/Float64Array#__get + local.get $1 + f64.const 100 local.get $0 - i32.const 128 - i32.lt_s - if - local.get $3 - local.get $0 - call $~lib/typedarray/Float64Array#__get - local.get $4 - local.get $0 - call $~lib/typedarray/Float64Array#__get - f64.sub - local.set $1 - local.get $3 - local.get $0 - local.get $3 - local.get $0 - call $~lib/typedarray/Float64Array#__get - local.get $1 - f64.const 100 - local.get $2 - local.get $11 - local.get $1 - f64.const 0 - f64.gt - select - f64.sub - f64.mul - f64.const 100 - f64.div - f64.sub - call $~lib/typedarray/Float64Array#__set - local.get $0 - i32.const 1 - i32.add - local.set $0 - br $for-loop|012 - end + local.get $11 + local.get $1 + f64.const 0 + f64.gt + select + f64.sub + f64.mul + f64.const 100 + f64.div + f64.sub + call $~lib/typedarray/Float64Array#__set + local.get $2 + i32.const 1 + i32.add + local.set $2 + br $for-loop|012 end - local.get $3 - call $~lib/rt/pure/__release - local.get $4 - call $~lib/rt/pure/__release end + local.get $3 + call $~lib/rt/pure/__release + local.get $4 + call $~lib/rt/pure/__release f64.const 1 - local.set $1 + local.set $0 loop $for-loop|02 local.get $8 i32.const 128 i32.lt_s if - local.get $1 - local.get $6 + local.get $0 + global.get $assembly/index/inputData local.get $8 call $~lib/typedarray/Float64Array#__get - local.tee $2 + local.tee $1 f64.gt if - local.get $2 - local.set $1 + local.get $1 + local.set $0 end - local.get $2 + local.get $1 local.get $9 - local.get $2 + local.get $1 local.get $9 f64.gt select @@ -3366,40 +3441,40 @@ i32.const 14 i32.lt_s if - local.get $13 - local.get $2 - global.get $assembly/index/audioProps - i32.const 0 + local.get $12 + local.get $1 + global.get $assembly/index/audioSettings + i32.const 9 call $~lib/typedarray/Float64Array#__get f64.mul f64.add - local.set $13 + local.set $12 else local.get $8 i32.const 69 i32.gt_s if - local.get $14 - local.get $2 - global.get $assembly/index/audioProps - i32.const 2 + local.get $13 + local.get $1 + global.get $assembly/index/audioSettings + i32.const 7 call $~lib/typedarray/Float64Array#__get f64.mul f64.add - local.set $14 + local.set $13 else - local.get $15 - local.get $2 - global.get $assembly/index/audioProps - i32.const 1 + local.get $14 + local.get $1 + global.get $assembly/index/audioSettings + i32.const 8 call $~lib/typedarray/Float64Array#__get f64.mul f64.add - local.set $15 + local.set $14 end end local.get $10 - local.get $2 + local.get $1 f64.add local.set $10 local.get $8 @@ -3413,135 +3488,121 @@ f64.const 0 local.get $9 global.get $assembly/index/audioSettings - i32.const 7 + i32.const 10 call $~lib/typedarray/Float64Array#__get f64.const 1e3 f64.div f64.lt select - local.set $2 - global.get $assembly/index/audioData - local.get $6 + local.set $1 + global.get $assembly/index/outputData + global.get $assembly/index/inputData call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> global.get $assembly/index/audioProps i32.const 16 i32.const 3 call $~lib/rt/pure/__new - local.tee $0 + local.tee $2 i32.const 80 i32.const 0 call $~lib/rt/pure/__new - local.tee $4 + local.tee $3 call $~lib/rt/pure/__retain i32.store - local.get $0 - local.get $4 + local.get $2 + local.get $3 i32.store offset=4 - local.get $0 + local.get $2 i32.const 80 i32.store offset=8 - local.get $0 + local.get $2 i32.const 10 i32.store offset=12 - local.get $0 + local.get $2 call $~lib/rt/pure/__retain - local.tee $4 + local.tee $3 i32.load offset=4 - local.tee $0 - local.get $13 + local.tee $2 + local.get $12 f64.store - local.get $0 - local.get $15 - f64.store offset=8 - local.get $0 + local.get $2 local.get $14 + f64.store offset=8 + local.get $2 + local.get $13 f64.store offset=16 - local.get $0 + local.get $2 local.get $10 f64.store offset=24 + local.get $2 local.get $0 - local.get $1 f64.store offset=32 - local.get $0 + local.get $2 local.get $9 f64.store offset=40 - local.get $0 + local.get $2 local.get $10 f64.const 0.0078125 f64.mul local.tee $10 f64.store offset=48 - local.get $0 + local.get $2 local.get $9 - local.get $1 + local.get $0 f64.sub f64.store offset=56 - local.get $0 local.get $2 + local.get $1 f64.store offset=64 - local.get $0 - local.get $13 + local.get $2 + local.get $12 f64.const 8 f64.mul - local.get $15 - f64.sub local.get $14 + f64.sub + local.get $13 f64.add f64.const 6 f64.div local.get $10 f64.div f64.store offset=72 - local.get $4 - call $~lib/rt/pure/__retain - local.set $8 - call $~lib/rt/pure/__retain - local.set $0 - local.get $8 - call $~lib/rt/pure/__retain - local.tee $3 - i32.load offset=12 - local.get $0 - i32.load offset=8 - i32.const 3 - i32.shr_u - i32.gt_s - if - i32.const 1952 - i32.const 2016 - i32.const 1775 - i32.const 47 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.load offset=4 local.get $3 - i32.load offset=4 + call $~lib/typedarray/Float64Array#set<~lib/array/Array> local.get $3 - i32.load offset=8 - call $~lib/memory/memory.copy - local.get $3 - call $~lib/rt/pure/__release - local.get $0 - call $~lib/rt/pure/__release - local.get $8 - call $~lib/rt/pure/__release - local.get $4 - call $~lib/rt/pure/__release - local.get $6 call $~lib/rt/pure/__release ) (func $~start + i32.const 64 + call $~lib/typedarray/Float64Array#constructor + global.set $assembly/index/pinkNoise + global.get $assembly/index/pinkNoise + i32.const 1792 + call $~lib/typedarray/Float64Array#set<~lib/array/Array> + i32.const 128 + call $~lib/typedarray/Float64Array#constructor + global.set $assembly/index/inputData + global.get $assembly/index/inputData + call $~lib/typedarray/Float64Array#fill + call $~lib/rt/pure/__release i32.const 128 call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/audioData + global.set $assembly/index/outputData + global.get $assembly/index/outputData + call $~lib/typedarray/Float64Array#fill + call $~lib/rt/pure/__release i32.const 10 call $~lib/typedarray/Float64Array#constructor global.set $assembly/index/audioProps - i32.const 8 + global.get $assembly/index/audioProps + call $~lib/typedarray/Float64Array#fill + call $~lib/rt/pure/__release + i32.const 11 call $~lib/typedarray/Float64Array#constructor global.set $assembly/index/audioSettings + global.get $assembly/index/audioSettings + call $~lib/typedarray/Float64Array#fill + call $~lib/rt/pure/__release ) (func $~lib/rt/pure/decrement (param $0 i32) (local $1 i32) @@ -3577,7 +3638,7 @@ i32.const 12 i32.add i32.load - br_table $__inlined_func$~lib/rt/__visit_members $__inlined_func$~lib/rt/__visit_members $~lib/arraybuffer/ArrayBufferView $folding-inner1 $folding-inner2 $folding-inner2 $folding-inner1 $invalid + br_table $__inlined_func$~lib/rt/__visit_members $__inlined_func$~lib/rt/__visit_members $~lib/arraybuffer/ArrayBufferView $folding-inner1 $folding-inner2 $folding-inner2 $folding-inner2 $folding-inner1 $invalid end local.get $0 i32.load offset=20 @@ -3639,12 +3700,9 @@ i32.store offset=4 end ) - (func $~lib/rt/pure/__collect - nop - ) (func $~lib/rt/pure/__visit (param $0 i32) local.get $0 - i32.const 2172 + i32.const 2132 i32.lt_u if return diff --git a/src/wasc/build/untouched.wat b/src/wasc/build/untouched.wat index cffafca..5801fb8 100644 --- a/src/wasc/build/untouched.wat +++ b/src/wasc/build/untouched.wat @@ -2,17 +2,18 @@ (type $i32_i32_=>_none (func (param i32 i32))) (type $i32_=>_none (func (param i32))) (type $i32_=>_i32 (func (param i32) (result i32))) - (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) - (type $none_=>_none (func)) + (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) - (type $i32_i32_=>_f64 (func (param i32 i32) (result f64))) + (type $none_=>_none (func)) (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) (type $i32_i32_f64_=>_none (func (param i32 i32 f64))) (type $i32_i32_f64_f64_=>_none (func (param i32 i32 f64 f64))) (type $i32_f64_=>_none (func (param i32 f64))) (type $i32_i32_i32_i32_=>_i32 (func (param i32 i32 i32 i32) (result i32))) + (type $i32_f64_i32_i32_=>_i32 (func (param i32 f64 i32 i32) (result i32))) (type $f64_=>_i32 (func (param f64) (result i32))) + (type $i32_i32_=>_f64 (func (param i32 i32) (result f64))) (type $f64_f64_=>_f64 (func (param f64 f64) (result f64))) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) @@ -25,10 +26,9 @@ (data (i32.const 844) "&\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00&\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00b\00u\00f\00f\00e\00r\00.\00t\00s\00") (data (i32.const 908) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00") (data (i32.const 972) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00~\00l\00i\00b\00/\00t\00y\00p\00e\00d\00a\00r\00r\00a\00y\00.\00t\00s\00") - (data (i32.const 1036) "\1a\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s\00") - (data (i32.const 1088) "\00\00\00\00\00\a0\f6?\00\00\00\00\00\00\00\00\00\c8\b9\f2\82,\d6\bf\80V7($\b4\fa<\00\00\00\00\00\80\f6?\00\00\00\00\00\00\00\00\00\08X\bf\bd\d1\d5\bf \f7\e0\d8\08\a5\1c\bd\00\00\00\00\00`\f6?\00\00\00\00\00\00\00\00\00XE\17wv\d5\bfmP\b6\d5\a4b#\bd\00\00\00\00\00@\f6?\00\00\00\00\00\00\00\00\00\f8-\87\ad\1a\d5\bf\d5g\b0\9e\e4\84\e6\bc\00\00\00\00\00 \f6?\00\00\00\00\00\00\00\00\00xw\95_\be\d4\bf\e0>)\93i\1b\04\bd\00\00\00\00\00\00\f6?\00\00\00\00\00\00\00\00\00`\1c\c2\8ba\d4\bf\cc\84LH/\d8\13=\00\00\00\00\00\e0\f5?\00\00\00\00\00\00\00\00\00\a8\86\860\04\d4\bf:\0b\82\ed\f3B\dc<\00\00\00\00\00\c0\f5?\00\00\00\00\00\00\00\00\00HiUL\a6\d3\bf`\94Q\86\c6\b1 =\00\00\00\00\00\a0\f5?\00\00\00\00\00\00\00\00\00\80\98\9a\ddG\d3\bf\92\80\c5\d4MY%=\00\00\00\00\00\80\f5?\00\00\00\00\00\00\00\00\00 \e1\ba\e2\e8\d2\bf\d8+\b7\99\1e{&=\00\00\00\00\00`\f5?\00\00\00\00\00\00\00\00\00\88\de\13Z\89\d2\bf?\b0\cf\b6\14\ca\15=\00\00\00\00\00`\f5?\00\00\00\00\00\00\00\00\00\88\de\13Z\89\d2\bf?\b0\cf\b6\14\ca\15=\00\00\00\00\00@\f5?\00\00\00\00\00\00\00\00\00x\cf\fbA)\d2\bfv\daS($Z\16\bd\00\00\00\00\00 \f5?\00\00\00\00\00\00\00\00\00\98i\c1\98\c8\d1\bf\04T\e7h\bc\af\1f\bd\00\00\00\00\00\00\f5?\00\00\00\00\00\00\00\00\00\a8\ab\ab\\g\d1\bf\f0\a8\823\c6\1f\1f=\00\00\00\00\00\e0\f4?\00\00\00\00\00\00\00\00\00H\ae\f9\8b\05\d1\bffZ\05\fd\c4\a8&\bd\00\00\00\00\00\c0\f4?\00\00\00\00\00\00\00\00\00\90s\e2$\a3\d0\bf\0e\03\f4~\eek\0c\bd\00\00\00\00\00\a0\f4?\00\00\00\00\00\00\00\00\00\d0\b4\94%@\d0\bf\7f-\f4\9e\b86\f0\bc\00\00\00\00\00\a0\f4?\00\00\00\00\00\00\00\00\00\d0\b4\94%@\d0\bf\7f-\f4\9e\b86\f0\bc\00\00\00\00\00\80\f4?\00\00\00\00\00\00\00\00\00@^m\18\b9\cf\bf\87<\99\ab*W\0d=\00\00\00\00\00`\f4?\00\00\00\00\00\00\00\00\00`\dc\cb\ad\f0\ce\bf$\af\86\9c\b7&+=\00\00\00\00\00@\f4?\00\00\00\00\00\00\00\00\00\f0*n\07\'\ce\bf\10\ff?TO/\17\bd\00\00\00\00\00 \f4?\00\00\00\00\00\00\00\00\00\c0Ok!\\\cd\bf\1bh\ca\bb\91\ba!=\00\00\00\00\00\00\f4?\00\00\00\00\00\00\00\00\00\a0\9a\c7\f7\8f\cc\bf4\84\9fhOy\'=\00\00\00\00\00\00\f4?\00\00\00\00\00\00\00\00\00\a0\9a\c7\f7\8f\cc\bf4\84\9fhOy\'=\00\00\00\00\00\e0\f3?\00\00\00\00\00\00\00\00\00\90-t\86\c2\cb\bf\8f\b7\8b1\b0N\19=\00\00\00\00\00\c0\f3?\00\00\00\00\00\00\00\00\00\c0\80N\c9\f3\ca\bff\90\cd?cN\ba<\00\00\00\00\00\a0\f3?\00\00\00\00\00\00\00\00\00\b0\e2\1f\bc#\ca\bf\ea\c1F\dcd\8c%\bd\00\00\00\00\00\a0\f3?\00\00\00\00\00\00\00\00\00\b0\e2\1f\bc#\ca\bf\ea\c1F\dcd\8c%\bd\00\00\00\00\00\80\f3?\00\00\00\00\00\00\00\00\00P\f4\9cZR\c9\bf\e3\d4\c1\04\d9\d1*\bd\00\00\00\00\00`\f3?\00\00\00\00\00\00\00\00\00\d0 e\a0\7f\c8\bf\t\fa\db\7f\bf\bd+=\00\00\00\00\00@\f3?\00\00\00\00\00\00\00\00\00\e0\10\02\89\ab\c7\bfXJSr\90\db+=\00\00\00\00\00@\f3?\00\00\00\00\00\00\00\00\00\e0\10\02\89\ab\c7\bfXJSr\90\db+=\00\00\00\00\00 \f3?\00\00\00\00\00\00\00\00\00\d0\19\e7\0f\d6\c6\bff\e2\b2\a3j\e4\10\bd\00\00\00\00\00\00\f3?\00\00\00\00\00\00\00\00\00\90\a7p0\ff\c5\bf9P\10\9fC\9e\1e\bd\00\00\00\00\00\00\f3?\00\00\00\00\00\00\00\00\00\90\a7p0\ff\c5\bf9P\10\9fC\9e\1e\bd\00\00\00\00\00\e0\f2?\00\00\00\00\00\00\00\00\00\b0\a1\e3\e5&\c5\bf\8f[\07\90\8b\de \bd\00\00\00\00\00\c0\f2?\00\00\00\00\00\00\00\00\00\80\cbl+M\c4\bf\11\0e\bd\00\00\00\00\00\e0\ed?\00\00\00\00\00\00\00\00\00`F\d1;\97\b1?\9b\9e\0dV]2%\bd\00\00\00\00\00\a0\ed?\00\00\00\00\00\00\00\00\00\e0\d1\a7\f5\bd\b3?\d7N\db\a5^\c8,=\00\00\00\00\00`\ed?\00\00\00\00\00\00\00\00\00\a0\97MZ\e9\b5?\1e\1d]<\06i,\bd\00\00\00\00\00@\ed?\00\00\00\00\00\00\00\00\00\c0\ea\n\d3\00\b7?2\ed\9d\a9\8d\1e\ec<\00\00\00\00\00\00\ed?\00\00\00\00\00\00\00\00\00@Y]^3\b9?\daG\bd:\\\11#=\00\00\00\00\00\c0\ec?\00\00\00\00\00\00\00\00\00`\ad\8d\c8j\bb?\e5h\f7+\80\90\13\bd\00\00\00\00\00\a0\ec?\00\00\00\00\00\00\00\00\00@\bc\01X\88\bc?\d3\acZ\c6\d1F&=\00\00\00\00\00`\ec?\00\00\00\00\00\00\00\00\00 \n\839\c7\be?\e0E\e6\afh\c0-\bd\00\00\00\00\00@\ec?\00\00\00\00\00\00\00\00\00\e0\db9\91\e8\bf?\fd\n\a1O\d64%\bd\00\00\00\00\00\00\ec?\00\00\00\00\00\00\00\00\00\e0\'\82\8e\17\c1?\f2\07-\cex\ef!=\00\00\00\00\00\e0\eb?\00\00\00\00\00\00\00\00\00\f0#~+\aa\c1?4\998D\8e\a7,=\00\00\00\00\00\a0\eb?\00\00\00\00\00\00\00\00\00\80\86\0ca\d1\c2?\a1\b4\81\cbl\9d\03=\00\00\00\00\00\80\eb?\00\00\00\00\00\00\00\00\00\90\15\b0\fce\c3?\89rK#\a8/\c6<\00\00\00\00\00@\eb?\00\00\00\00\00\00\00\00\00\b03\83=\91\c4?x\b6\fdTy\83%=\00\00\00\00\00 \eb?\00\00\00\00\00\00\00\00\00\b0\a1\e4\e5\'\c5?\c7}i\e5\e83&=\00\00\00\00\00\e0\ea?\00\00\00\00\00\00\00\00\00\10\8c\beNW\c6?x.<,\8b\cf\19=\00\00\00\00\00\c0\ea?\00\00\00\00\00\00\00\00\00pu\8b\12\f0\c6?\e1!\9c\e5\8d\11%\bd\00\00\00\00\00\a0\ea?\00\00\00\00\00\00\00\00\00PD\85\8d\89\c7?\05C\91p\10f\1c\bd\00\00\00\00\00`\ea?\00\00\00\00\00\00\00\00\00\009\eb\af\be\c8?\d1,\e9\aaT=\07\bd\00\00\00\00\00@\ea?\00\00\00\00\00\00\00\00\00\00\f7\dcZZ\c9?o\ff\a0X(\f2\07=\00\00\00\00\00\00\ea?\00\00\00\00\00\00\00\00\00\e0\8a<\ed\93\ca?i!VPCr(\bd\00\00\00\00\00\e0\e9?\00\00\00\00\00\00\00\00\00\d0[W\d81\cb?\aa\e1\acN\8d5\0c\bd\00\00\00\00\00\c0\e9?\00\00\00\00\00\00\00\00\00\e0;8\87\d0\cb?\b6\12TY\c4K-\bd\00\00\00\00\00\a0\e9?\00\00\00\00\00\00\00\00\00\10\f0\c6\fbo\cc?\d2+\96\c5r\ec\f1\bc\00\00\00\00\00`\e9?\00\00\00\00\00\00\00\00\00\90\d4\b0=\b1\cd?5\b0\15\f7*\ff*\bd\00\00\00\00\00@\e9?\00\00\00\00\00\00\00\00\00\10\e7\ff\0eS\ce?0\f4A`\'\12\c2<\00\00\00\00\00 \e9?\00\00\00\00\00\00\00\00\00\00\dd\e4\ad\f5\ce?\11\8e\bbe\15!\ca\bc\00\00\00\00\00\00\e9?\00\00\00\00\00\00\00\00\00\b0\b3l\1c\99\cf?0\df\0c\ca\ec\cb\1b=\00\00\00\00\00\c0\e8?\00\00\00\00\00\00\00\00\00XM`8q\d0?\91N\ed\16\db\9c\f8<\00\00\00\00\00\a0\e8?\00\00\00\00\00\00\00\00\00`ag-\c4\d0?\e9\ea<\16\8b\18\'=\00\00\00\00\00\80\e8?\00\00\00\00\00\00\00\00\00\e8\'\82\8e\17\d1?\1c\f0\a5c\0e!,\bd\00\00\00\00\00`\e8?\00\00\00\00\00\00\00\00\00\f8\ac\cb\\k\d1?\81\16\a5\f7\cd\9a+=\00\00\00\00\00@\e8?\00\00\00\00\00\00\00\00\00hZc\99\bf\d1?\b7\bdGQ\ed\a6,=\00\00\00\00\00 \e8?\00\00\00\00\00\00\00\00\00\b8\0emE\14\d2?\ea\baF\ba\de\87\n=\00\00\00\00\00\e0\e7?\00\00\00\00\00\00\00\00\00\90\dc|\f0\be\d2?\f4\04PJ\fa\9c*=\00\00\00\00\00\c0\e7?\00\00\00\00\00\00\00\00\00`\d3\e1\f1\14\d3?\b8\9a\ec\ef?\d1f\87\10z^\90\bc\85\7fn\e8\15\e3\ef?\13\f6g5R\d2\8c\be\ef?m{\83]\a6\9a\97<\0f\89\f9lX\b5\ef?\fc\ef\fd\92\1a\b5\8e<\f7Gr+\92\ac\ef?\d1\9c/p=\be><\a2\d1\d32\ec\a3\ef?\0bn\90\894\03j\bc\1b\d3\fe\aff\9b\ef?\0e\bd/*RV\95\bcQ[\12\d0\01\93\ef?U\eaN\8c\ef\80P\bc\cc1l\c0\bd\8a\ef?\16\f4\d5\b9#\c9\91\bc\e0-\a9\ae\9a\82\ef?\afU\\\e9\e3\d3\80\f7\ec\9a<\aa\b9h1\87T\ef?\9d8\86\cb\82\e7\8f\bc\1d\d9\fc\"PM\ef?\8d\c3\a6DAo\8a<\d6\8cb\88;F\ef?}\04\e4\b0\05z\80<\96\dc}\91I?\ef?\94\a8\a8\e3\fd\8e\96<8bunz8\ef?}Ht\f2\18^\87\a9\af\0c\ef?\b6\ab\b0MuM\83<\15\b71\n\fe\06\ef?Lt\ac\e2\01B\86<1\d8L\fcp\01\ef?J\f8\d3]9\dd\8f<\ff\16d\b2\08\fc\ee?\04[\8e;\80\a3\86\bc\f1\9f\92_\c5\f6\ee?hPK\cc\edJ\92\bc\cb\a9:7\a7\f1\ee?\8e-Q\1b\f8\07\99\bcf\d8\05m\ae\ec\ee?\d26\94>\e8\d1q\bc\f7\9f\e54\db\e7\ee?\15\1b\ce\b3\19\19\99\bc\e5\a8\13\c3-\e3\ee?mL*\a7H\9f\85<\"4\12L\a6\de\ee?\8ai(z`\12\93\bc\1c\80\ac\04E\da\ee?[\89\17H\8f\a7X\bc*.\f7!\n\d6\ee?\1b\9aIg\9b,|\bc\97\a8P\d9\f5\d1\ee?\11\ac\c2`\edcC<-\89a`\08\ce\ee?\efd\06;\tf\96Z~d\1fx\bct_\ec\e8u\9f\ee?\b0}\8b\c0J\ee\86\bct\81\a5H\9a\9f\ee?\8a\e6U\1e2\19\86\bc\c9gBV\eb\9f\ee?\d3\d4\t^\cb\9c\90T\'\a4\ee?47;\f1\b6i\93\bc\13\ceL\99\89\a5\ee?\1e\ff\19:\84^\80\bc\ad\c7#F\1a\a7\ee?nWr\d8P\d4\94\bc\ed\92D\9b\d9\a8\ee?\00\8a\0e[g\ad\90<\99f\8a\d9\c7\aa\ee?\b4\ea\f0\c1/\b7\8d<\db\a0*B\e5\ac\ee?\ff\e7\c5\9c`\b6e\bc\8cD\b5\162\af\ee?D_\f3Y\83\f6{<6w\15\99\ae\b1\ee?\83=\1e\a7\1f\t\93\bc\c6\ff\91\0b[\b4\ee?)\1el\8b\b8\a9]\bc\e5\c5\cd\b07\b7\ee?Y\b9\90|\f9#l\bc\0fR\c8\cbD\ba\ee?\aa\f9\f4\"CC\92\bcPN\de\9f\82\bd\ee?K\8ef\d7l\ca\85\bc\ba\07\cap\f1\c0\ee?\'\ce\91+\fc\afq<\90\f0\a3\82\91\c4\ee?\bbs\n\e15\d2m<##\e3\19c\c8\ee?c\"b\"\04\c5\87\bce\e5]{f\cc\ee?\d51\e2\e3\86\1c\8b<3-J\ec\9b\d0\ee?\15\bb\bc\d3\d1\bb\91\bc]%>\b2\03\d5\ee?\d21\ee\9c1\cc\90\b4\07!\d5\82\bc_\9b{3\97|\ef?\c9\0dG;\b9*\89\bc)\a1\f5\14F\86\ef?\d3\88:`\04\b6t<\f6?\8b\e7.\90\ef?qr\9dQ\ec\c5\83<\83L\c7\fbQ\9a\ef?\f0\91\d3\8f\12\f7\8f\bc\da\90\a4\a2\af\a4\ef?}t#\e2\98\ae\8d\bc\f1g\8e-H\af\ef?\08 \aaA\bc\c3\8e<\'Za\ee\1b\ba\ef?2\eb\a9\c3\94+\84<\97\bak7+\c5\ef?\ee\85\d11\a9d\8a<@En[v\d0\ef?\ed\e3;\e4\ba7\8e\bc\14\be\9c\ad\fd\db\ef?\9d\cd\91M;\89w<\d8\90\9e\81\c1\e7\ef?\89\cc`A\c1\05S<\f1q\8f+\c2\f3\ef?") - (data (i32.const 7232) "\07\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\"\1a\00\00\00\00\00\00!\1a\00\00\02\00\00\00a\00\00\00\02\00\00\00\"\t\00\00\00\00\00\00") + (data (i32.const 1032) "\00\00\00\00\00\a0\f6?\00\00\00\00\00\00\00\00\00\c8\b9\f2\82,\d6\bf\80V7($\b4\fa<\00\00\00\00\00\80\f6?\00\00\00\00\00\00\00\00\00\08X\bf\bd\d1\d5\bf \f7\e0\d8\08\a5\1c\bd\00\00\00\00\00`\f6?\00\00\00\00\00\00\00\00\00XE\17wv\d5\bfmP\b6\d5\a4b#\bd\00\00\00\00\00@\f6?\00\00\00\00\00\00\00\00\00\f8-\87\ad\1a\d5\bf\d5g\b0\9e\e4\84\e6\bc\00\00\00\00\00 \f6?\00\00\00\00\00\00\00\00\00xw\95_\be\d4\bf\e0>)\93i\1b\04\bd\00\00\00\00\00\00\f6?\00\00\00\00\00\00\00\00\00`\1c\c2\8ba\d4\bf\cc\84LH/\d8\13=\00\00\00\00\00\e0\f5?\00\00\00\00\00\00\00\00\00\a8\86\860\04\d4\bf:\0b\82\ed\f3B\dc<\00\00\00\00\00\c0\f5?\00\00\00\00\00\00\00\00\00HiUL\a6\d3\bf`\94Q\86\c6\b1 =\00\00\00\00\00\a0\f5?\00\00\00\00\00\00\00\00\00\80\98\9a\ddG\d3\bf\92\80\c5\d4MY%=\00\00\00\00\00\80\f5?\00\00\00\00\00\00\00\00\00 \e1\ba\e2\e8\d2\bf\d8+\b7\99\1e{&=\00\00\00\00\00`\f5?\00\00\00\00\00\00\00\00\00\88\de\13Z\89\d2\bf?\b0\cf\b6\14\ca\15=\00\00\00\00\00`\f5?\00\00\00\00\00\00\00\00\00\88\de\13Z\89\d2\bf?\b0\cf\b6\14\ca\15=\00\00\00\00\00@\f5?\00\00\00\00\00\00\00\00\00x\cf\fbA)\d2\bfv\daS($Z\16\bd\00\00\00\00\00 \f5?\00\00\00\00\00\00\00\00\00\98i\c1\98\c8\d1\bf\04T\e7h\bc\af\1f\bd\00\00\00\00\00\00\f5?\00\00\00\00\00\00\00\00\00\a8\ab\ab\\g\d1\bf\f0\a8\823\c6\1f\1f=\00\00\00\00\00\e0\f4?\00\00\00\00\00\00\00\00\00H\ae\f9\8b\05\d1\bffZ\05\fd\c4\a8&\bd\00\00\00\00\00\c0\f4?\00\00\00\00\00\00\00\00\00\90s\e2$\a3\d0\bf\0e\03\f4~\eek\0c\bd\00\00\00\00\00\a0\f4?\00\00\00\00\00\00\00\00\00\d0\b4\94%@\d0\bf\7f-\f4\9e\b86\f0\bc\00\00\00\00\00\a0\f4?\00\00\00\00\00\00\00\00\00\d0\b4\94%@\d0\bf\7f-\f4\9e\b86\f0\bc\00\00\00\00\00\80\f4?\00\00\00\00\00\00\00\00\00@^m\18\b9\cf\bf\87<\99\ab*W\0d=\00\00\00\00\00`\f4?\00\00\00\00\00\00\00\00\00`\dc\cb\ad\f0\ce\bf$\af\86\9c\b7&+=\00\00\00\00\00@\f4?\00\00\00\00\00\00\00\00\00\f0*n\07\'\ce\bf\10\ff?TO/\17\bd\00\00\00\00\00 \f4?\00\00\00\00\00\00\00\00\00\c0Ok!\\\cd\bf\1bh\ca\bb\91\ba!=\00\00\00\00\00\00\f4?\00\00\00\00\00\00\00\00\00\a0\9a\c7\f7\8f\cc\bf4\84\9fhOy\'=\00\00\00\00\00\00\f4?\00\00\00\00\00\00\00\00\00\a0\9a\c7\f7\8f\cc\bf4\84\9fhOy\'=\00\00\00\00\00\e0\f3?\00\00\00\00\00\00\00\00\00\90-t\86\c2\cb\bf\8f\b7\8b1\b0N\19=\00\00\00\00\00\c0\f3?\00\00\00\00\00\00\00\00\00\c0\80N\c9\f3\ca\bff\90\cd?cN\ba<\00\00\00\00\00\a0\f3?\00\00\00\00\00\00\00\00\00\b0\e2\1f\bc#\ca\bf\ea\c1F\dcd\8c%\bd\00\00\00\00\00\a0\f3?\00\00\00\00\00\00\00\00\00\b0\e2\1f\bc#\ca\bf\ea\c1F\dcd\8c%\bd\00\00\00\00\00\80\f3?\00\00\00\00\00\00\00\00\00P\f4\9cZR\c9\bf\e3\d4\c1\04\d9\d1*\bd\00\00\00\00\00`\f3?\00\00\00\00\00\00\00\00\00\d0 e\a0\7f\c8\bf\t\fa\db\7f\bf\bd+=\00\00\00\00\00@\f3?\00\00\00\00\00\00\00\00\00\e0\10\02\89\ab\c7\bfXJSr\90\db+=\00\00\00\00\00@\f3?\00\00\00\00\00\00\00\00\00\e0\10\02\89\ab\c7\bfXJSr\90\db+=\00\00\00\00\00 \f3?\00\00\00\00\00\00\00\00\00\d0\19\e7\0f\d6\c6\bff\e2\b2\a3j\e4\10\bd\00\00\00\00\00\00\f3?\00\00\00\00\00\00\00\00\00\90\a7p0\ff\c5\bf9P\10\9fC\9e\1e\bd\00\00\00\00\00\00\f3?\00\00\00\00\00\00\00\00\00\90\a7p0\ff\c5\bf9P\10\9fC\9e\1e\bd\00\00\00\00\00\e0\f2?\00\00\00\00\00\00\00\00\00\b0\a1\e3\e5&\c5\bf\8f[\07\90\8b\de \bd\00\00\00\00\00\c0\f2?\00\00\00\00\00\00\00\00\00\80\cbl+M\c4\bf\11\0e\bd\00\00\00\00\00\e0\ed?\00\00\00\00\00\00\00\00\00`F\d1;\97\b1?\9b\9e\0dV]2%\bd\00\00\00\00\00\a0\ed?\00\00\00\00\00\00\00\00\00\e0\d1\a7\f5\bd\b3?\d7N\db\a5^\c8,=\00\00\00\00\00`\ed?\00\00\00\00\00\00\00\00\00\a0\97MZ\e9\b5?\1e\1d]<\06i,\bd\00\00\00\00\00@\ed?\00\00\00\00\00\00\00\00\00\c0\ea\n\d3\00\b7?2\ed\9d\a9\8d\1e\ec<\00\00\00\00\00\00\ed?\00\00\00\00\00\00\00\00\00@Y]^3\b9?\daG\bd:\\\11#=\00\00\00\00\00\c0\ec?\00\00\00\00\00\00\00\00\00`\ad\8d\c8j\bb?\e5h\f7+\80\90\13\bd\00\00\00\00\00\a0\ec?\00\00\00\00\00\00\00\00\00@\bc\01X\88\bc?\d3\acZ\c6\d1F&=\00\00\00\00\00`\ec?\00\00\00\00\00\00\00\00\00 \n\839\c7\be?\e0E\e6\afh\c0-\bd\00\00\00\00\00@\ec?\00\00\00\00\00\00\00\00\00\e0\db9\91\e8\bf?\fd\n\a1O\d64%\bd\00\00\00\00\00\00\ec?\00\00\00\00\00\00\00\00\00\e0\'\82\8e\17\c1?\f2\07-\cex\ef!=\00\00\00\00\00\e0\eb?\00\00\00\00\00\00\00\00\00\f0#~+\aa\c1?4\998D\8e\a7,=\00\00\00\00\00\a0\eb?\00\00\00\00\00\00\00\00\00\80\86\0ca\d1\c2?\a1\b4\81\cbl\9d\03=\00\00\00\00\00\80\eb?\00\00\00\00\00\00\00\00\00\90\15\b0\fce\c3?\89rK#\a8/\c6<\00\00\00\00\00@\eb?\00\00\00\00\00\00\00\00\00\b03\83=\91\c4?x\b6\fdTy\83%=\00\00\00\00\00 \eb?\00\00\00\00\00\00\00\00\00\b0\a1\e4\e5\'\c5?\c7}i\e5\e83&=\00\00\00\00\00\e0\ea?\00\00\00\00\00\00\00\00\00\10\8c\beNW\c6?x.<,\8b\cf\19=\00\00\00\00\00\c0\ea?\00\00\00\00\00\00\00\00\00pu\8b\12\f0\c6?\e1!\9c\e5\8d\11%\bd\00\00\00\00\00\a0\ea?\00\00\00\00\00\00\00\00\00PD\85\8d\89\c7?\05C\91p\10f\1c\bd\00\00\00\00\00`\ea?\00\00\00\00\00\00\00\00\00\009\eb\af\be\c8?\d1,\e9\aaT=\07\bd\00\00\00\00\00@\ea?\00\00\00\00\00\00\00\00\00\00\f7\dcZZ\c9?o\ff\a0X(\f2\07=\00\00\00\00\00\00\ea?\00\00\00\00\00\00\00\00\00\e0\8a<\ed\93\ca?i!VPCr(\bd\00\00\00\00\00\e0\e9?\00\00\00\00\00\00\00\00\00\d0[W\d81\cb?\aa\e1\acN\8d5\0c\bd\00\00\00\00\00\c0\e9?\00\00\00\00\00\00\00\00\00\e0;8\87\d0\cb?\b6\12TY\c4K-\bd\00\00\00\00\00\a0\e9?\00\00\00\00\00\00\00\00\00\10\f0\c6\fbo\cc?\d2+\96\c5r\ec\f1\bc\00\00\00\00\00`\e9?\00\00\00\00\00\00\00\00\00\90\d4\b0=\b1\cd?5\b0\15\f7*\ff*\bd\00\00\00\00\00@\e9?\00\00\00\00\00\00\00\00\00\10\e7\ff\0eS\ce?0\f4A`\'\12\c2<\00\00\00\00\00 \e9?\00\00\00\00\00\00\00\00\00\00\dd\e4\ad\f5\ce?\11\8e\bbe\15!\ca\bc\00\00\00\00\00\00\e9?\00\00\00\00\00\00\00\00\00\b0\b3l\1c\99\cf?0\df\0c\ca\ec\cb\1b=\00\00\00\00\00\c0\e8?\00\00\00\00\00\00\00\00\00XM`8q\d0?\91N\ed\16\db\9c\f8<\00\00\00\00\00\a0\e8?\00\00\00\00\00\00\00\00\00`ag-\c4\d0?\e9\ea<\16\8b\18\'=\00\00\00\00\00\80\e8?\00\00\00\00\00\00\00\00\00\e8\'\82\8e\17\d1?\1c\f0\a5c\0e!,\bd\00\00\00\00\00`\e8?\00\00\00\00\00\00\00\00\00\f8\ac\cb\\k\d1?\81\16\a5\f7\cd\9a+=\00\00\00\00\00@\e8?\00\00\00\00\00\00\00\00\00hZc\99\bf\d1?\b7\bdGQ\ed\a6,=\00\00\00\00\00 \e8?\00\00\00\00\00\00\00\00\00\b8\0emE\14\d2?\ea\baF\ba\de\87\n=\00\00\00\00\00\e0\e7?\00\00\00\00\00\00\00\00\00\90\dc|\f0\be\d2?\f4\04PJ\fa\9c*=\00\00\00\00\00\c0\e7?\00\00\00\00\00\00\00\00\00`\d3\e1\f1\14\d3?\b8\9a\ec\ef?\d1f\87\10z^\90\bc\85\7fn\e8\15\e3\ef?\13\f6g5R\d2\8c\be\ef?m{\83]\a6\9a\97<\0f\89\f9lX\b5\ef?\fc\ef\fd\92\1a\b5\8e<\f7Gr+\92\ac\ef?\d1\9c/p=\be><\a2\d1\d32\ec\a3\ef?\0bn\90\894\03j\bc\1b\d3\fe\aff\9b\ef?\0e\bd/*RV\95\bcQ[\12\d0\01\93\ef?U\eaN\8c\ef\80P\bc\cc1l\c0\bd\8a\ef?\16\f4\d5\b9#\c9\91\bc\e0-\a9\ae\9a\82\ef?\afU\\\e9\e3\d3\80\f7\ec\9a<\aa\b9h1\87T\ef?\9d8\86\cb\82\e7\8f\bc\1d\d9\fc\"PM\ef?\8d\c3\a6DAo\8a<\d6\8cb\88;F\ef?}\04\e4\b0\05z\80<\96\dc}\91I?\ef?\94\a8\a8\e3\fd\8e\96<8bunz8\ef?}Ht\f2\18^\87\a9\af\0c\ef?\b6\ab\b0MuM\83<\15\b71\n\fe\06\ef?Lt\ac\e2\01B\86<1\d8L\fcp\01\ef?J\f8\d3]9\dd\8f<\ff\16d\b2\08\fc\ee?\04[\8e;\80\a3\86\bc\f1\9f\92_\c5\f6\ee?hPK\cc\edJ\92\bc\cb\a9:7\a7\f1\ee?\8e-Q\1b\f8\07\99\bcf\d8\05m\ae\ec\ee?\d26\94>\e8\d1q\bc\f7\9f\e54\db\e7\ee?\15\1b\ce\b3\19\19\99\bc\e5\a8\13\c3-\e3\ee?mL*\a7H\9f\85<\"4\12L\a6\de\ee?\8ai(z`\12\93\bc\1c\80\ac\04E\da\ee?[\89\17H\8f\a7X\bc*.\f7!\n\d6\ee?\1b\9aIg\9b,|\bc\97\a8P\d9\f5\d1\ee?\11\ac\c2`\edcC<-\89a`\08\ce\ee?\efd\06;\tf\96Z~d\1fx\bct_\ec\e8u\9f\ee?\b0}\8b\c0J\ee\86\bct\81\a5H\9a\9f\ee?\8a\e6U\1e2\19\86\bc\c9gBV\eb\9f\ee?\d3\d4\t^\cb\9c\90T\'\a4\ee?47;\f1\b6i\93\bc\13\ceL\99\89\a5\ee?\1e\ff\19:\84^\80\bc\ad\c7#F\1a\a7\ee?nWr\d8P\d4\94\bc\ed\92D\9b\d9\a8\ee?\00\8a\0e[g\ad\90<\99f\8a\d9\c7\aa\ee?\b4\ea\f0\c1/\b7\8d<\db\a0*B\e5\ac\ee?\ff\e7\c5\9c`\b6e\bc\8cD\b5\162\af\ee?D_\f3Y\83\f6{<6w\15\99\ae\b1\ee?\83=\1e\a7\1f\t\93\bc\c6\ff\91\0b[\b4\ee?)\1el\8b\b8\a9]\bc\e5\c5\cd\b07\b7\ee?Y\b9\90|\f9#l\bc\0fR\c8\cbD\ba\ee?\aa\f9\f4\"CC\92\bcPN\de\9f\82\bd\ee?K\8ef\d7l\ca\85\bc\ba\07\cap\f1\c0\ee?\'\ce\91+\fc\afq<\90\f0\a3\82\91\c4\ee?\bbs\n\e15\d2m<##\e3\19c\c8\ee?c\"b\"\04\c5\87\bce\e5]{f\cc\ee?\d51\e2\e3\86\1c\8b<3-J\ec\9b\d0\ee?\15\bb\bc\d3\d1\bb\91\bc]%>\b2\03\d5\ee?\d21\ee\9c1\cc\90\b4\07!\d5\82\bc_\9b{3\97|\ef?\c9\0dG;\b9*\89\bc)\a1\f5\14F\86\ef?\d3\88:`\04\b6t<\f6?\8b\e7.\90\ef?qr\9dQ\ec\c5\83<\83L\c7\fbQ\9a\ef?\f0\91\d3\8f\12\f7\8f\bc\da\90\a4\a2\af\a4\ef?}t#\e2\98\ae\8d\bc\f1g\8e-H\af\ef?\08 \aaA\bc\c3\8e<\'Za\ee\1b\ba\ef?2\eb\a9\c3\94+\84<\97\bak7+\c5\ef?\ee\85\d11\a9d\8a<@En[v\d0\ef?\ed\e3;\e4\ba7\8e\bc\14\be\9c\ad\fd\db\ef?\9d\cd\91M;\89w<\d8\90\9e\81\c1\e7\ef?\89\cc`A\c1\05S<\f1q\8f+\c2\f3\ef?") + (data (i32.const 7184) "\08\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\"\1a\00\00\00\00\00\00!\1a\00\00\02\00\00\00a\00\00\00\02\00\00\00!\01\00\00\02\00\00\00\"\t\00\00\00\00\00\00") (table $0 1 funcref) (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) (global $~lib/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0)) @@ -36,39 +36,26 @@ (global $assembly/index/DAT_LEN i32 (i32.const 128)) (global $assembly/index/HLF_LEN i32 (i32.const 64)) (global $assembly/index/QRT_LEN i32 (i32.const 32)) - (global $assembly/index/pinkNoise i32 (i32.const 768)) - (global $assembly/index/audioData (mut i32) (i32.const 0)) + (global $assembly/index/pNoise i32 (i32.const 768)) + (global $assembly/index/pinkNoise (mut i32) (i32.const 0)) + (global $assembly/index/inputData (mut i32) (i32.const 0)) + (global $~lib/builtins/i32.MAX_VALUE i32 (i32.const 2147483647)) + (global $assembly/index/outputData (mut i32) (i32.const 0)) (global $assembly/index/audioProps (mut i32) (i32.const 0)) - (global $assembly/index/Props.bass i32 (i32.const 0)) - (global $assembly/index/Props.mids i32 (i32.const 1)) - (global $assembly/index/Props.highs i32 (i32.const 2)) - (global $assembly/index/Props.sum i32 (i32.const 3)) - (global $assembly/index/Props.min i32 (i32.const 4)) - (global $assembly/index/Props.max i32 (i32.const 5)) - (global $assembly/index/Props.average i32 (i32.const 6)) - (global $assembly/index/Props.range i32 (i32.const 7)) - (global $assembly/index/Props.silent i32 (i32.const 8)) - (global $assembly/index/Props.intensity i32 (i32.const 9)) (global $assembly/index/audioSettings (mut i32) (i32.const 0)) - (global $assembly/index/Sett.equalize i32 (i32.const 0)) - (global $assembly/index/Sett.mono_audio i32 (i32.const 1)) - (global $assembly/index/Sett.audio_direction i32 (i32.const 2)) - (global $assembly/index/Sett.peak_filter i32 (i32.const 3)) - (global $assembly/index/Sett.value_smoothing i32 (i32.const 4)) - (global $assembly/index/Sett.audio_increase i32 (i32.const 5)) - (global $assembly/index/Sett.audio_decrease i32 (i32.const 6)) - (global $assembly/index/Sett.minimum_volume i32 (i32.const 7)) (global $~lib/util/math/log_tail (mut f64) (f64.const 0)) - (global $~lib/rt/__rtti_base i32 (i32.const 7232)) - (global $~lib/memory/__heap_base i32 (i32.const 7292)) + (global $~lib/rt/__rtti_base i32 (i32.const 7184)) + (global $~lib/memory/__heap_base i32 (i32.const 7252)) (export "memory" (memory $0)) (export "__new" (func $~lib/rt/pure/__new)) (export "__renew" (func $~lib/rt/pure/__renew)) (export "__retain" (func $~lib/rt/pure/__retain)) (export "__release" (func $~lib/rt/pure/__release)) - (export "__collect" (func $~lib/rt/pure/__collect)) (export "__rtti_base" (global $~lib/rt/__rtti_base)) - (export "audioData" (global $assembly/index/audioData)) + (export "allocF64Array" (func $assembly/index/allocF64Array)) + (export "allocU32Array" (func $assembly/index/allocU32Array)) + (export "inputData" (global $assembly/index/inputData)) + (export "outputData" (global $assembly/index/outputData)) (export "audioProps" (global $assembly/index/audioProps)) (export "audioSettings" (global $assembly/index/audioSettings)) (export "update" (func $assembly/index/update)) @@ -3334,21 +3321,278 @@ local.set $0 local.get $0 ) + (func $~lib/array/Array#get:length (param $0 i32) (result i32) + local.get $0 + i32.load offset=12 + ) + (func $~lib/typedarray/Float64Array#get:length (param $0 i32) (result i32) + local.get $0 + i32.load offset=8 + i32.const 3 + i32.shr_u + ) + (func $~lib/typedarray/Float64Array#set<~lib/array/Array> (param $0 i32) (param $1 i32) (param $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + local.get $1 + call $~lib/rt/pure/__retain + local.set $1 + local.get $0 + call $~lib/rt/pure/__retain + local.set $5 + local.get $1 + call $~lib/rt/pure/__retain + local.set $4 + local.get $2 + local.set $3 + i32.const 0 + drop + local.get $3 + i32.const 0 + i32.lt_s + if + i32.const 928 + i32.const 992 + i32.const 1774 + i32.const 19 + call $~lib/builtins/abort + unreachable + end + local.get $4 + call $~lib/array/Array#get:length + local.get $3 + i32.add + local.get $5 + call $~lib/typedarray/Float64Array#get:length + i32.gt_s + if + i32.const 928 + i32.const 992 + i32.const 1775 + i32.const 47 + call $~lib/builtins/abort + unreachable + end + i32.const 0 + i32.const 0 + i32.eq + if (result i32) + i32.const 3 + i32.const 3 + i32.eq + else + i32.const 0 + end + if (result i32) + i32.const 0 + if (result i32) + i32.const 0 + else + i32.const 0 + end + i32.eqz + else + i32.const 0 + end + drop + local.get $5 + i32.load offset=4 + local.get $3 + i32.const 3 + i32.shl + i32.add + local.get $4 + i32.load offset=4 + local.get $4 + i32.load offset=8 + call $~lib/memory/memory.copy + local.get $4 + call $~lib/rt/pure/__release + local.get $5 + call $~lib/rt/pure/__release + local.get $1 + call $~lib/rt/pure/__release + ) + (func $~lib/typedarray/Float64Array#fill (param $0 i32) (param $1 f64) (param $2 i32) (param $3 i32) (result i32) + (local $4 i32) + (local $5 i32) + (local $6 f64) + (local $7 i32) + (local $8 i32) + (local $9 i32) + (local $10 i32) + (local $11 i32) + local.get $0 + call $~lib/rt/pure/__retain + local.set $7 + local.get $1 + local.set $6 + local.get $2 + local.set $5 + local.get $3 + local.set $4 + local.get $7 + i32.load offset=4 + local.set $8 + local.get $7 + call $~lib/typedarray/Float64Array#get:length + local.set $9 + local.get $5 + i32.const 0 + i32.lt_s + if (result i32) + local.get $9 + local.get $5 + i32.add + local.tee $10 + i32.const 0 + local.tee $11 + local.get $10 + local.get $11 + i32.gt_s + select + else + local.get $5 + local.tee $11 + local.get $9 + local.tee $10 + local.get $11 + local.get $10 + i32.lt_s + select + end + local.set $5 + local.get $4 + i32.const 0 + i32.lt_s + if (result i32) + local.get $9 + local.get $4 + i32.add + local.tee $10 + i32.const 0 + local.tee $11 + local.get $10 + local.get $11 + i32.gt_s + select + else + local.get $4 + local.tee $11 + local.get $9 + local.tee $10 + local.get $11 + local.get $10 + i32.lt_s + select + end + local.set $4 + i32.const 8 + i32.const 1 + i32.eq + drop + loop $for-loop|0 + local.get $5 + local.get $4 + i32.lt_s + local.set $11 + local.get $11 + if + local.get $8 + local.get $5 + i32.const 3 + i32.shl + i32.add + local.get $6 + f64.store + local.get $5 + i32.const 1 + i32.add + local.set $5 + br $for-loop|0 + end + end + local.get $7 + ) (func $start:assembly/index (local $0 i32) (local $1 i32) i32.const 0 + global.get $assembly/index/HLF_LEN + call $~lib/typedarray/Float64Array#constructor + global.set $assembly/index/pinkNoise + global.get $assembly/index/pinkNoise + global.get $assembly/index/pNoise + i32.const 0 + call $~lib/typedarray/Float64Array#set<~lib/array/Array> + i32.const 0 global.get $assembly/index/DAT_LEN call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/audioData + global.set $assembly/index/inputData + global.get $assembly/index/inputData + f64.const 0 + i32.const 0 + global.get $~lib/builtins/i32.MAX_VALUE + call $~lib/typedarray/Float64Array#fill + call $~lib/rt/pure/__release + i32.const 0 + global.get $assembly/index/DAT_LEN + call $~lib/typedarray/Float64Array#constructor + global.set $assembly/index/outputData + global.get $assembly/index/outputData + f64.const 0 + i32.const 0 + global.get $~lib/builtins/i32.MAX_VALUE + call $~lib/typedarray/Float64Array#fill + call $~lib/rt/pure/__release i32.const 0 i32.const 10 call $~lib/typedarray/Float64Array#constructor global.set $assembly/index/audioProps + global.get $assembly/index/audioProps + f64.const 0 i32.const 0 - i32.const 8 + global.get $~lib/builtins/i32.MAX_VALUE + call $~lib/typedarray/Float64Array#fill + call $~lib/rt/pure/__release + i32.const 0 + i32.const 11 call $~lib/typedarray/Float64Array#constructor global.set $assembly/index/audioSettings + global.get $assembly/index/audioSettings + f64.const 0 + i32.const 0 + global.get $~lib/builtins/i32.MAX_VALUE + call $~lib/typedarray/Float64Array#fill + call $~lib/rt/pure/__release + ) + (func $assembly/index/allocF64Array (param $0 i32) (result i32) + i32.const 0 + local.get $0 + call $~lib/typedarray/Float64Array#constructor + ) + (func $~lib/typedarray/Uint32Array#constructor (param $0 i32) (param $1 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 12 + i32.const 6 + call $~lib/rt/pure/__new + call $~lib/rt/pure/__retain + local.set $0 + end + local.get $0 + local.get $1 + i32.const 2 + call $~lib/arraybuffer/ArrayBufferView#constructor + local.set $0 + local.get $0 + ) + (func $assembly/index/allocU32Array (param $0 i32) (result i32) + i32.const 0 + local.get $0 + call $~lib/typedarray/Uint32Array#constructor ) (func $~lib/typedarray/Float64Array#__get (param $0 i32) (param $1 i32) (result f64) local.get $1 @@ -3378,37 +3622,6 @@ f64.const 0 f64.gt ) - (func $~lib/array/Array#__uget (param $0 i32) (param $1 i32) (result f64) - local.get $0 - i32.load offset=4 - local.get $1 - i32.const 3 - i32.shl - i32.add - f64.load - ) - (func $~lib/array/Array#__get (param $0 i32) (param $1 i32) (result f64) - (local $2 f64) - local.get $1 - local.get $0 - i32.load offset=12 - i32.ge_u - if - i32.const 928 - i32.const 1056 - i32.const 104 - i32.const 42 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $1 - call $~lib/array/Array#__uget - local.set $2 - i32.const 0 - drop - local.get $2 - ) (func $~lib/typedarray/Float64Array#__set (param $0 i32) (param $1 i32) (param $2 f64) local.get $1 local.get $0 @@ -3433,12 +3646,6 @@ local.get $2 f64.store ) - (func $~lib/typedarray/Float64Array#get:length (param $0 i32) (result i32) - local.get $0 - i32.load offset=8 - i32.const 3 - i32.shr_u - ) (func $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) (local $4 i32) @@ -3549,7 +3756,7 @@ call $~lib/typedarray/Float64Array#__get global.get $assembly/index/pinkNoise local.get $2 - call $~lib/array/Array#__get + call $~lib/typedarray/Float64Array#__get f64.div call $~lib/typedarray/Float64Array#__set local.get $1 @@ -3563,7 +3770,7 @@ call $~lib/typedarray/Float64Array#__get global.get $assembly/index/pinkNoise local.get $2 - call $~lib/array/Array#__get + call $~lib/typedarray/Float64Array#__get f64.div call $~lib/typedarray/Float64Array#__set local.get $2 @@ -3670,12 +3877,16 @@ local.get $1 local.get $0 global.get $assembly/index/DAT_LEN + i32.const 1 + i32.sub local.get $1 i32.sub call $~lib/typedarray/Float64Array#__get call $~lib/typedarray/Float64Array#__set local.get $0 global.get $assembly/index/DAT_LEN + i32.const 1 + i32.sub local.get $1 i32.sub local.get $3 @@ -3714,12 +3925,16 @@ local.get $1 local.get $0 global.get $assembly/index/HLF_LEN + i32.const 1 + i32.sub local.get $1 i32.sub call $~lib/typedarray/Float64Array#__get call $~lib/typedarray/Float64Array#__set local.get $0 global.get $assembly/index/HLF_LEN + i32.const 1 + i32.sub local.get $1 i32.sub local.get $3 @@ -3762,12 +3977,16 @@ i32.add local.get $0 global.get $assembly/index/DAT_LEN + i32.const 1 + i32.sub local.get $1 i32.sub call $~lib/typedarray/Float64Array#__get call $~lib/typedarray/Float64Array#__set local.get $0 global.get $assembly/index/DAT_LEN + i32.const 1 + i32.sub local.get $1 i32.sub local.get $3 @@ -4288,7 +4507,7 @@ local.get $13 f64.convert_i64_s local.set $15 - i32.const 1088 + i32.const 1032 local.get $12 i32.const 2 i32.const 3 @@ -4297,7 +4516,7 @@ i32.add f64.load local.set $16 - i32.const 1088 + i32.const 1032 local.get $12 i32.const 2 i32.const 3 @@ -4306,7 +4525,7 @@ i32.add f64.load offset=16 local.set $17 - i32.const 1088 + i32.const 1032 local.get $12 i32.const 2 i32.const 3 @@ -4604,7 +4823,7 @@ i64.sub i64.shl local.set $13 - i32.const 5184 + i32.const 5128 local.get $40 i32.const 3 i32.shl @@ -4612,7 +4831,7 @@ i64.load f64.reinterpret_i64 local.set $25 - i32.const 5184 + i32.const 5128 local.get $40 i32.const 3 i32.shl @@ -5063,97 +5282,8 @@ i32.store offset=12 local.get $4 ) - (func $~lib/array/Array#get:length (param $0 i32) (result i32) - local.get $0 - i32.load offset=12 - ) - (func $~lib/typedarray/Float64Array#set<~lib/array/Array> (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - local.get $1 - call $~lib/rt/pure/__retain - local.set $1 - local.get $0 - call $~lib/rt/pure/__retain - local.set $5 - local.get $1 - call $~lib/rt/pure/__retain - local.set $4 - local.get $2 - local.set $3 - i32.const 0 - drop - local.get $3 - i32.const 0 - i32.lt_s - if - i32.const 928 - i32.const 992 - i32.const 1774 - i32.const 19 - call $~lib/builtins/abort - unreachable - end - local.get $4 - call $~lib/array/Array#get:length - local.get $3 - i32.add - local.get $5 - call $~lib/typedarray/Float64Array#get:length - i32.gt_s - if - i32.const 928 - i32.const 992 - i32.const 1775 - i32.const 47 - call $~lib/builtins/abort - unreachable - end - i32.const 0 - i32.const 0 - i32.eq - if (result i32) - i32.const 3 - i32.const 3 - i32.eq - else - i32.const 0 - end - if (result i32) - i32.const 0 - if (result i32) - i32.const 0 - else - i32.const 0 - end - i32.eqz - else - i32.const 0 - end - drop - local.get $5 - i32.load offset=4 - local.get $3 - i32.const 3 - i32.shl - i32.add - local.get $4 - i32.load offset=4 - local.get $4 - i32.load offset=8 - call $~lib/memory/memory.copy - local.get $4 - call $~lib/rt/pure/__release - local.get $5 - call $~lib/rt/pure/__release - local.get $1 - call $~lib/rt/pure/__release - ) - (func $~lib/gc/gc.collect - call $~lib/rt/pure/__collect - ) - (func $assembly/index/update (param $0 i32) + (func $assembly/index/update + (local $0 f64) (local $1 f64) (local $2 f64) (local $3 f64) @@ -5168,88 +5298,85 @@ (local $12 f64) (local $13 f64) (local $14 i32) - local.get $0 - call $~lib/rt/pure/__retain - local.set $0 global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.equalize + i32.const 0 call $~lib/typedarray/Float64Array#__get call $assembly/index/isOn if - local.get $0 + global.get $assembly/index/inputData call $assembly/index/correctPinkNoise end global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.mono_audio + i32.const 1 call $~lib/typedarray/Float64Array#__get call $assembly/index/isOn if - local.get $0 + global.get $assembly/index/inputData call $assembly/index/stereoToMono end global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.audio_direction + i32.const 2 call $~lib/typedarray/Float64Array#__get call $assembly/index/isOn if global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.mono_audio + i32.const 1 call $~lib/typedarray/Float64Array#__get call $assembly/index/isOn if - local.get $0 + global.get $assembly/index/inputData call $assembly/index/invertAll else - local.get $0 + global.get $assembly/index/inputData call $assembly/index/invertFirst end else global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.mono_audio + i32.const 1 call $~lib/typedarray/Float64Array#__get call $assembly/index/isOn if - local.get $0 + global.get $assembly/index/inputData call $assembly/index/invertSecond end end global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.peak_filter + i32.const 3 call $~lib/typedarray/Float64Array#__get call $assembly/index/isOn if - local.get $0 + global.get $assembly/index/inputData global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.peak_filter + i32.const 3 call $~lib/typedarray/Float64Array#__get f64.const 1 f64.add call $assembly/index/peakFilter end global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.value_smoothing + i32.const 4 call $~lib/typedarray/Float64Array#__get call $assembly/index/isOn if - local.get $0 + global.get $assembly/index/inputData global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.value_smoothing + i32.const 4 call $~lib/typedarray/Float64Array#__get + local.set $0 + local.get $0 + f64.floor i32.trunc_f64_s call $assembly/index/smoothArray end - global.get $assembly/index/audioData - if - local.get $0 - global.get $assembly/index/audioData - global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.audio_increase - call $~lib/typedarray/Float64Array#__get - global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.audio_decrease - call $~lib/typedarray/Float64Array#__get - call $assembly/index/applyValueLeveling - end + global.get $assembly/index/inputData + global.get $assembly/index/outputData + global.get $assembly/index/audioSettings + i32.const 5 + call $~lib/typedarray/Float64Array#__get + global.get $assembly/index/audioSettings + i32.const 6 + call $~lib/typedarray/Float64Array#__get + call $assembly/index/applyValueLeveling f64.const 0 local.set $1 f64.const 1 @@ -5271,7 +5398,7 @@ local.set $8 local.get $8 if - local.get $0 + global.get $assembly/index/inputData local.get $7 call $~lib/typedarray/Float64Array#__get local.set $9 @@ -5297,8 +5424,8 @@ if local.get $4 local.get $9 - global.get $assembly/index/audioProps - global.get $assembly/index/Props.bass + global.get $assembly/index/audioSettings + i32.const 9 call $~lib/typedarray/Float64Array#__get f64.mul f64.add @@ -5310,8 +5437,8 @@ if local.get $6 local.get $9 - global.get $assembly/index/audioProps - global.get $assembly/index/Props.highs + global.get $assembly/index/audioSettings + i32.const 7 call $~lib/typedarray/Float64Array#__get f64.mul f64.add @@ -5319,8 +5446,8 @@ else local.get $5 local.get $9 - global.get $assembly/index/audioProps - global.get $assembly/index/Props.mids + global.get $assembly/index/audioSettings + i32.const 8 call $~lib/typedarray/Float64Array#__get f64.mul f64.add @@ -5345,7 +5472,7 @@ local.set $10 local.get $3 global.get $assembly/index/audioSettings - global.get $assembly/index/Sett.minimum_volume + i32.const 10 call $~lib/typedarray/Float64Array#__get f64.const 1e3 f64.div @@ -5372,8 +5499,8 @@ local.get $2 f64.sub local.set $13 - global.get $assembly/index/audioData - local.get $0 + global.get $assembly/index/outputData + global.get $assembly/index/inputData i32.const 0 call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> global.get $assembly/index/audioProps @@ -5421,11 +5548,8 @@ local.tee $14 i32.const 0 call $~lib/typedarray/Float64Array#set<~lib/array/Array> - call $~lib/gc/gc.collect local.get $14 call $~lib/rt/pure/__release - local.get $0 - call $~lib/rt/pure/__release ) (func $~start call $start:assembly/index @@ -5523,11 +5647,6 @@ i32.store offset=4 end ) - (func $~lib/rt/pure/__collect - i32.const 1 - drop - return - ) (func $~lib/rt/pure/__visit (param $0 i32) (param $1 i32) local.get $0 global.get $~lib/memory/__heap_base @@ -5596,6 +5715,11 @@ local.get $1 call $~lib/arraybuffer/ArrayBufferView~visit ) + (func $~lib/typedarray/Uint32Array~visit (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + call $~lib/arraybuffer/ArrayBufferView~visit + ) (func $~lib/array/Array#__visit (param $0 i32) (param $1 i32) i32.const 0 drop @@ -5612,46 +5736,52 @@ (func $~lib/rt/__visit_members (param $0 i32) (param $1 i32) block $invalid block $~lib/array/Array - block $~lib/typedarray/Uint8ClampedArray - block $~lib/typedarray/Float64Array - block $~lib/array/Array - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer + block $~lib/typedarray/Uint32Array + block $~lib/typedarray/Uint8ClampedArray + block $~lib/typedarray/Float64Array + block $~lib/array/Array + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $~lib/array/Array $~lib/typedarray/Float64Array $~lib/typedarray/Uint8ClampedArray $~lib/typedarray/Uint32Array $~lib/array/Array $invalid + end local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $~lib/array/Array $~lib/typedarray/Float64Array $~lib/typedarray/Uint8ClampedArray $~lib/array/Array $invalid + local.get $1 + call $~lib/arraybuffer/ArrayBuffer~visit + return end local.get $0 local.get $1 - call $~lib/arraybuffer/ArrayBuffer~visit + call $~lib/string/String~visit return end local.get $0 local.get $1 - call $~lib/string/String~visit + call $~lib/arraybuffer/ArrayBufferView~visit return end local.get $0 local.get $1 - call $~lib/arraybuffer/ArrayBufferView~visit + call $~lib/array/Array~visit return end local.get $0 local.get $1 - call $~lib/array/Array~visit + call $~lib/typedarray/Float64Array~visit return end local.get $0 local.get $1 - call $~lib/typedarray/Float64Array~visit + call $~lib/typedarray/Uint8ClampedArray~visit return end local.get $0 local.get $1 - call $~lib/typedarray/Uint8ClampedArray~visit + call $~lib/typedarray/Uint32Array~visit return end local.get $0 diff --git a/src/wasc/index.js b/src/wasc/index.js index 04bc346..708043e 100644 --- a/src/wasc/index.js +++ b/src/wasc/index.js @@ -2,4 +2,7 @@ const fs = require("fs"); const loader = require("@assemblyscript/loader"); const imports = { /* imports go here */ }; const wasmModule = loader.instantiateSync(fs.readFileSync(__dirname + "/build/optimized.wasm"), imports); -module.exports = wasmModule.exports; +//module.exports = wasmModule.exports; + + +var res = wasmModule.exports.update(new Float32Array(128)); \ No newline at end of file diff --git a/src/wasc/package.json b/src/wasc/package.json index 7287ec5..d44b5f3 100644 --- a/src/wasc/package.json +++ b/src/wasc/package.json @@ -1,7 +1,7 @@ { "scripts": { - "asbuild:untouched": "asc assembly/index.ts --target debug", - "asbuild:optimized": "asc assembly/index.ts --target release", + "asbuild:untouched": "asc assembly/index.ts --runtime full --target debug", + "asbuild:optimized": "asc assembly/index.ts --runtime full --target release", "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized", "test": "node tests" }, diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 1cc892d..58dc995 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -21,6 +21,9 @@ * It will automatically start to receive and process the audio data * which can then be accessed on the global object. * + * @todo + * - use worker run instead of multiple messages + * */ import { CComponent } from "../CComponent"; @@ -28,9 +31,9 @@ import { CSettings } from "../CSettings"; import { Ready } from "../Ready"; import { Smallog } from "../Smallog"; -import WEASWorker from 'worker-loader!./WEASWorker'; +import wascModule from '../wasc-worker'; -import wasmWorker from 'wasm-worker'; +const DAT_LEN = 128; export class WEASettings extends CSettings { audioprocessing: boolean = true; @@ -55,8 +58,34 @@ export class WEASettings extends CSettings { minimum_volume: number = 0.005; } -export class WEAS extends CComponent { +enum Sett { + equalize = 0, + mono_audio = 1, + audio_direction = 2, + peak_filter = 3, + value_smoothing = 4, + audio_increase = 5, + audio_decrease = 6, + treble_multiplier = 7, + mids_multiplier = 8, + bass_multiplier = 9, + minimum_volume = 10, +} +enum Props { + bass = 0, + mids = 1, + highs = 2, + sum = 3, + min = 4, + max = 5, + average = 6, + range = 7, + silent = 8, + intensity = 9 +} + +export class WEAS extends CComponent { // last processed audio object public lastAudio = null; @@ -64,9 +93,12 @@ export class WEAS extends CComponent { // settings object public settings: WEASettings = new WEASettings(); + // web assembly functions + private run: any = null; + constructor() { super(); - // delay audio initialization for some time + // delay audio initialization Ready.On(() => this.realInit()); } @@ -79,49 +111,119 @@ export class WEAS extends CComponent { var self = this; - wasmWorker('WEAS.wasm') - .then(module => { + wascModule('WEAS.wasm', {}, false) + .then(r => { + // save module context + console.log(r); + const { module, instance, exports, run } = r as any; + this.run = run + + const inBuff = new Float64Array(DAT_LEN); + + // pass settings to module + this.updateSettings(); - window['wallpaperRegisterAudioListener']((audioArray) => { + // register audio callback on module + window['wallpaperRegisterAudioListener'](async audioArray => { // check proof if (!audioArray) return; - if (audioArray.length != 128) { + if (audioArray.length != DAT_LEN) { Smallog.Error("audioListener: received invalid audio data array. Length: " + audioArray.length); return; } - let audBuff = new Float32Array(audioArray); - Smallog.Debug("Send Data to Worker: " + JSON.stringify(audioArray)); - - // post web worker task - module.run(({ - importObject, - instance, - params - } - // the code ran in web worker - ) => { - //const sum = instance.exports.message(...params); - const sum = instance.exports.message(params.time); - return 'got time message: ' + sum; - }, - // the object posted to web worker - { - settings: this.GetSettingsObj(), - last: Object.assign({}, this.lastAudio), - time: performance.now() / 1000, - audio: audBuff.buffer - }) - // the result from web worker - .then(e => { - e.data.data = new Float32Array(e.data.data); - self.lastAudio = e.data; - Smallog.Debug("Got Data from Worker! Offset= " + (performance.now() / 1000 - e.data.time) + ", Data= " + JSON.stringify(e.data)); - }); + + // prepare Transfer object + const start = performance.now(); + inBuff.set(audioArray); + + // WRAP UPDATE IN RUN + this.run( + // isolated Function ran inside worker + ({ module, instance, importObject, params }) => { + const { exports } = instance; + const { data } = params[0]; + + console.debug("Send Data to Worker: " + JSON.stringify(data)); + + // apply data to module memory + const transfer = importObject.__getFloat64ArrayView(exports.inputData); + transfer.set(data); + // actual audio data processing + exports.update(); + + // get updates Data & Properties + return { + data: importObject.__getFloat64ArrayView(exports.outputData), + props: importObject.__getFloat64ArrayView(exports.audioProps), + } + }, + // Data passed to worker + { + data: inBuff + }) + .then( + // worker result, back in main context + (result) => { + const { data, props } = result; + // apply actual last Data from worker + self.lastAudio = { + time: start, + data, + ...this.getProps(props) // @TODO make props to Props mapping + }; + + // print info + Smallog.Debug("Got Data from Worker! Time= " + (performance.now() - start) + ", Data= " + JSON.stringify(data)); + + } + ); }); }).catch(ex => Smallog.Error(JSON.stringify(ex))); } + private getProps(dProps: ArrayLike) { + var keys = Object.keys(Props); + keys = keys.slice(keys.length / 2); + var res = {}; + for (var index = 0; index < keys.length; index++) { + const key = keys[index]; + res[key] = dProps[Props[key]]; + } + return res; + } + + // CAVEAT: only available after init and module load + public async updateSettings() { + if (!this.run) return; + const start = performance.now(); + + var keys = Object.keys(Sett); + keys = keys.slice(keys.length / 2); + const sett = new Float64Array(keys.length); + for (let index = 0; index < keys.length; index++) { + const key = keys[index]; + sett[Sett[key]] = this.settings[key] || 0; + } + + // WRAP IN RUN + this.run( + // isolated Function ran inside worker + ({ module, instance, importObject, params }) => { + const { exports } = instance; + const { data } = params[0]; + + const transfer = importObject.__getFloat64ArrayView(exports.audioSettings); + transfer.set(data); + + console.debug("Send Settings to Worker: " + JSON.stringify(data)); + }, + // Data passed to worker + { + data: sett + }); + } + public hasAudio() { // return false if there is no data or its invalid due to time (> 3s old) return this.lastAudio && !this.lastAudio.silent && diff --git a/src/weas/WEAS.wasm.asc b/src/weas/WEAS.wasm.asc index dc25421..fc748e6 100644 --- a/src/weas/WEAS.wasm.asc +++ b/src/weas/WEAS.wasm.asc @@ -10,11 +10,46 @@ * Wallaper Engine Audio Supplier worker. */ -const DAT_LEN = 128; -const HLF_LEN = 64; -const QRT_LEN = 32; -const pinkNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, + ////////////////////////// + // CUSTOM API + ////////////////////////// + +@external("env", "logf") +declare function logf(value: f64): void; + +@external("env", "logi") +declare function logi(value: u32): void; + +@external("env", "logU32Array") +declare function logU32Array(arr: Uint32Array): void; + +@external("env", "logF64Array") +declare function logF64Array(arr: Float64Array): void; + +export function allocF64Array(length: i32): Float64Array { + return new Float64Array(length); +} + +export function allocU32Array(length: i32): Uint32Array { + return new Uint32Array(length); +} + +@inline +function deallocArray(arr: T[]): void { + memory.free(changetype(arr.buffer_)); + memory.free(changetype(arr)); +} + + ////////////////////////// + // Main Program + ////////////////////////// + +const DAT_LEN: i32 = 128; +const HLF_LEN: i32 = 64; +const QRT_LEN: i32 = 32; + +const pNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, 0.5452348949654, 0.50723325864167, 0.4677726234682, 0.44204182748767, 0.41956517802157, 0.41517375040002, 0.41312118577934, 0.40618363960446, 0.39913707474975, 0.38207008614508, 0.38329789106488, 0.37472136606245, 0.36586428412968, 0.37603017335105, 0.39762590761573, @@ -27,6 +62,9 @@ const pinkNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.637679 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, 2.4043566176474, 2.4280476777842, 2.3917477397336, 2.4032522546622, 2.3614180150678]; +const pinkNoise = new Float64Array(HLF_LEN); +pinkNoise.set(pNoise); + // correct pink noise for first and second half function correctPinkNoise(data: Float64Array): void { @@ -53,8 +91,8 @@ function stereoToMono(data: Float64Array): void { function invertFirst(data: Float64Array): void { for (var i = 0; i < QRT_LEN; i++) { var a = data[i]; - data[i] = data[HLF_LEN - i]; - data[HLF_LEN - i] = a; + data[i] = data[HLF_LEN - 1 - i]; + data[HLF_LEN - 1 - i] = a; } } @@ -62,8 +100,8 @@ function invertFirst(data: Float64Array): void { function invertSecond(data: Float64Array): void { for (var i = 0; i < QRT_LEN; i++) { var b = data[HLF_LEN + i]; - data[HLF_LEN + i] = data[DAT_LEN - i]; - data[DAT_LEN - i] = b; + data[HLF_LEN + i] = data[DAT_LEN - 1 - i]; + data[DAT_LEN - 1 - i] = b; } } @@ -71,8 +109,8 @@ function invertSecond(data: Float64Array): void { function invertAll(data: Float64Array): void { for (var i = 0; i < HLF_LEN; i++) { var a = data[i]; - data[i] = data[DAT_LEN - i]; - data[DAT_LEN - i] = a; + data[i] = data[DAT_LEN - 1 - i]; + data[DAT_LEN - 1 - i] = a; } } @@ -119,40 +157,25 @@ function applyValueLeveling(curr: Float64Array, prev: Float64Array, inc: f64, de } } +// THEW INPUT VALUE TO WRITE TO +export const inputData = new Float64Array(DAT_LEN); +inputData.fill(0.0); + // this will hold the current processed audio data // either: B-H | H-B | HL-BL-BR-HR | BL-HL-HR-BR // where ( B=bass, H=high, L=left, R=right ) // in range > 0.0 and ~< 1.5 -export const audioData = new Float64Array(DAT_LEN); +export const outputData = new Float64Array(DAT_LEN); +outputData.fill(0.0); // this will hold the current processed properties: export const audioProps = new Float64Array(10); -enum Props { - bass = 0, - mids = 1, - highs = 2, - sum = 3, - min = 4, - max = 5, - average = 6, - range = 7, - silent = 8, - intensity = 9 -} - +audioProps.fill(0.0); // this will hold the current processing settings -export const audioSettings = new Float64Array(8); -enum Sett { - equalize = 0, - mono_audio = 1, - audio_direction = 2, - peak_filter = 3, - value_smoothing = 4, - audio_increase = 5, - audio_decrease = 6, - minimum_volume = 7 -} +export const audioSettings = new Float64Array(11); +audioSettings.fill(0.0); + // check helper function isOn(a: f64): boolean { @@ -160,48 +183,49 @@ function isOn(a: f64): boolean { } // this will update and process new data -export function update(newData: Float64Array): void { +export function update(): void { // fix pink noise? - if (isOn(audioSettings[Sett.equalize])) - correctPinkNoise(newData); + if (isOn(audioSettings[0])) + correctPinkNoise(inputData); // write botch channels to mono - if (isOn(audioSettings[Sett.mono_audio])) - stereoToMono(newData); + if (isOn(audioSettings[1])) + stereoToMono(inputData); - if (isOn(audioSettings[Sett.audio_direction])) { + if (isOn(audioSettings[2])) { // flipped high & low mapping - if (isOn(audioSettings[Sett.mono_audio])) + if (isOn(audioSettings[1])) // flip whole range - invertAll(newData); + invertAll(inputData); else { // only flip first half of stereo - invertFirst(newData); + invertFirst(inputData); } - } else { // normal high & low mapping - if (isOn(audioSettings[Sett.mono_audio])) { + if (isOn(audioSettings[1])) { // only flip the second half of the data - invertSecond(newData); + invertSecond(inputData); } } // process peaks? - if (isOn(audioSettings[Sett.peak_filter])) - peakFilter(newData, audioSettings[Sett.peak_filter] + 1); + if (isOn(audioSettings[3])) + peakFilter(inputData, audioSettings[3] + 1); // smooth data? - if (isOn(audioSettings[Sett.value_smoothing])) - smoothArray(newData, audioSettings[Sett.value_smoothing] as i32); + if (isOn(audioSettings[4])) + smoothArray(inputData, Math.floor(audioSettings[4]) as i32); - // process with last data? - if (audioData) - applyValueLeveling(newData, audioData, - audioSettings[Sett.audio_increase], - audioSettings[Sett.audio_decrease]); + //logF64Array(inputData); + //logF64Array(outputData); + + // process with last data + applyValueLeveling(inputData, outputData, + audioSettings[5], + audioSettings[6]); // process current frequency data and previous if given var sum: f64 = 0, @@ -213,40 +237,26 @@ export function update(newData: Float64Array): void { for (var indx = 0; indx < DAT_LEN; indx++) { // parse current freq value - var idata: f64 = newData[indx]; + var idata: f64 = inputData[indx]; // process min max value if (idata < min) min = idata; if (idata > max) max = idata; // process ranges - if (indx < (42 / 3)) bass += idata * audioProps[Props.bass]; - else if (indx > 69) peaks += idata * audioProps[Props.highs]; - else mids += idata * audioProps[Props.mids]; + if (indx < (42 / 3)) bass += idata * audioSettings[9]; + else if (indx > 69) peaks += idata * audioSettings[7]; + else mids += idata * audioSettings[8]; // calc peak average sum += idata; } // calc average with previous entry var average: f64 = sum / (DAT_LEN as f64); - var silent: f64 = (max < audioSettings[Sett.minimum_volume] / 1000) ? 0.9999 : 0.00; + var silent: f64 = (max < audioSettings[10] / 1000) ? 0.9999 : 0.00; var intensity: f64 = (bass * 8 - mids + peaks) / 6 / average; var range: f64 = max - min; // Apply Data - audioData.set(newData); - audioProps.set([ - bass, - mids, - peaks, - sum, - min, - max, - average, - range, - silent, - intensity - ]); - - // clean - gc.collect(); + outputData.set(inputData); + audioProps.set([bass,mids,peaks,sum,min,max,average,range,silent,intensity]) + // DONE } - diff --git a/src/worker-loader.d.ts b/src/worker-loader.d.ts index 2e3c733..c67af6b 100644 --- a/src/worker-loader.d.ts +++ b/src/worker-loader.d.ts @@ -1,7 +1,7 @@ declare module 'worker-loader!*' { // You need to change `Worker`, if you specified a different value for the `workerType` option class WebpackWorker extends Worker { - constructor(); + constructor(options?: any); } // Uncomment this if you set the `esModule` option to `false` From cb49b24b5b2c2d996518e1acef8466283b1f8463 Mon Sep 17 00:00:00 2001 From: hexxone Date: Mon, 28 Dec 2020 01:40:00 +0100 Subject: [PATCH 16/76] fixed naming --- src/{wasc => wasc-builder}/WascPlugin.js | 0 src/{wasc => wasc-builder}/asconfig.json | 0 src/{wasc => wasc-builder}/assembly/index.asc | 0 src/{wasc => wasc-builder}/assembly/index.ts | 0 src/{wasc => wasc-builder}/assembly/tsconfig.json | 0 src/{wasc => wasc-builder}/build/optimized.wat | 0 src/{wasc => wasc-builder}/build/untouched.wat | 0 src/{wasc => wasc-builder}/index.js | 0 src/{wasc => wasc-builder}/package-lock.json | 0 src/{wasc => wasc-builder}/package.json | 0 src/{wasc => wasc-builder}/tests/index.js | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename src/{wasc => wasc-builder}/WascPlugin.js (100%) rename src/{wasc => wasc-builder}/asconfig.json (100%) rename src/{wasc => wasc-builder}/assembly/index.asc (100%) rename src/{wasc => wasc-builder}/assembly/index.ts (100%) rename src/{wasc => wasc-builder}/assembly/tsconfig.json (100%) rename src/{wasc => wasc-builder}/build/optimized.wat (100%) rename src/{wasc => wasc-builder}/build/untouched.wat (100%) rename src/{wasc => wasc-builder}/index.js (100%) rename src/{wasc => wasc-builder}/package-lock.json (100%) rename src/{wasc => wasc-builder}/package.json (100%) rename src/{wasc => wasc-builder}/tests/index.js (100%) diff --git a/src/wasc/WascPlugin.js b/src/wasc-builder/WascPlugin.js similarity index 100% rename from src/wasc/WascPlugin.js rename to src/wasc-builder/WascPlugin.js diff --git a/src/wasc/asconfig.json b/src/wasc-builder/asconfig.json similarity index 100% rename from src/wasc/asconfig.json rename to src/wasc-builder/asconfig.json diff --git a/src/wasc/assembly/index.asc b/src/wasc-builder/assembly/index.asc similarity index 100% rename from src/wasc/assembly/index.asc rename to src/wasc-builder/assembly/index.asc diff --git a/src/wasc/assembly/index.ts b/src/wasc-builder/assembly/index.ts similarity index 100% rename from src/wasc/assembly/index.ts rename to src/wasc-builder/assembly/index.ts diff --git a/src/wasc/assembly/tsconfig.json b/src/wasc-builder/assembly/tsconfig.json similarity index 100% rename from src/wasc/assembly/tsconfig.json rename to src/wasc-builder/assembly/tsconfig.json diff --git a/src/wasc/build/optimized.wat b/src/wasc-builder/build/optimized.wat similarity index 100% rename from src/wasc/build/optimized.wat rename to src/wasc-builder/build/optimized.wat diff --git a/src/wasc/build/untouched.wat b/src/wasc-builder/build/untouched.wat similarity index 100% rename from src/wasc/build/untouched.wat rename to src/wasc-builder/build/untouched.wat diff --git a/src/wasc/index.js b/src/wasc-builder/index.js similarity index 100% rename from src/wasc/index.js rename to src/wasc-builder/index.js diff --git a/src/wasc/package-lock.json b/src/wasc-builder/package-lock.json similarity index 100% rename from src/wasc/package-lock.json rename to src/wasc-builder/package-lock.json diff --git a/src/wasc/package.json b/src/wasc-builder/package.json similarity index 100% rename from src/wasc/package.json rename to src/wasc-builder/package.json diff --git a/src/wasc/tests/index.js b/src/wasc-builder/tests/index.js similarity index 100% rename from src/wasc/tests/index.js rename to src/wasc-builder/tests/index.js From ec9e34c2c6b9f404e72ce994a476be11fa491d18 Mon Sep 17 00:00:00 2001 From: hexxone Date: Mon, 28 Dec 2020 01:41:54 +0100 Subject: [PATCH 17/76] make embedded run promised --- src/wasc-worker | 2 +- src/weas/WEAS.ts | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/wasc-worker b/src/wasc-worker index 8b905f1..e23fd12 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 8b905f11e17ef8aa87d5cd96e85169bab5f24e64 +Subproject commit e23fd128135600f5ce722688e5511502d91eee17 diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 58dc995..04cc64e 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -116,8 +116,8 @@ export class WEAS extends CComponent { // save module context console.log(r); const { module, instance, exports, run } = r as any; - this.run = run - + this.run = run; + // create transfer buffer const inBuff = new Float64Array(DAT_LEN); // pass settings to module @@ -132,7 +132,7 @@ export class WEAS extends CComponent { return; } - // prepare Transfer object + // prepare data const start = performance.now(); inBuff.set(audioArray); @@ -143,7 +143,7 @@ export class WEAS extends CComponent { const { exports } = instance; const { data } = params[0]; - console.debug("Send Data to Worker: " + JSON.stringify(data)); + //console.debug("Send Data to Worker: " + JSON.stringify(data)); // apply data to module memory const transfer = importObject.__getFloat64ArrayView(exports.inputData); @@ -196,7 +196,6 @@ export class WEAS extends CComponent { // CAVEAT: only available after init and module load public async updateSettings() { if (!this.run) return; - const start = performance.now(); var keys = Object.keys(Sett); keys = keys.slice(keys.length / 2); @@ -207,7 +206,7 @@ export class WEAS extends CComponent { } // WRAP IN RUN - this.run( + await this.run( // isolated Function ran inside worker ({ module, instance, importObject, params }) => { const { exports } = instance; From 81a09b379365dc865415680a53b31145338ab583 Mon Sep 17 00:00:00 2001 From: hexxone Date: Sat, 9 Jan 2021 16:07:36 +0100 Subject: [PATCH 18/76] WebAssembly Plugin improvements --- src/wasc-builder/WascPlugin.js | 78 ++++----- src/wasc-builder/assembly/index.asc | 252 ---------------------------- src/wasc-builder/index.js | 8 - src/wasc-worker | 2 +- src/weas/WEAS.ts | 156 ++++++++--------- src/weas/WEASWorker.ts | 199 ---------------------- 6 files changed, 111 insertions(+), 584 deletions(-) delete mode 100644 src/wasc-builder/assembly/index.asc delete mode 100644 src/wasc-builder/index.js delete mode 100644 src/weas/WEASWorker.ts diff --git a/src/wasc-builder/WascPlugin.js b/src/wasc-builder/WascPlugin.js index 3029696..527964c 100644 --- a/src/wasc-builder/WascPlugin.js +++ b/src/wasc-builder/WascPlugin.js @@ -12,19 +12,15 @@ * */ - const fs = require("fs"); const path = require('path'); +const asc = require("assemblyscript/bin/asc"); const validate = require('schema-utils'); const { RawSource } = require('webpack-sources'); -const { execSync } = require('child_process'); const pluginName = 'WasmPlugin'; -const sourcePath = path.resolve(__dirname, 'assembly/index.ts'); -const optPath = path.resolve(__dirname, 'build/optimized.wasm'); -const untPath = path.resolve(__dirname, 'build/untouched.wasm'); -const untMap = path.resolve(__dirname, 'build/untouched.wasm.map'); +const outPath = path.resolve(__dirname, 'build'); // schema for options object const schema = { @@ -57,34 +53,37 @@ function getAllFiles(baseDir, subDir, arrayOfFiles) { return arrayOfFiles; } - -// compile assemblyscript (typescript) module to wasm and return binary -function compileWasm(inputPath, production) { +// compile assemblyscript (typescript) module to wasm and return binary +function compileWasm(inputPath, newName, production) { return new Promise(resolve => { - // copy .wasm.ts -> index.ts - // File destination.txt will be created or overwritten by default. - fs.copyFile(inputPath, sourcePath, (err) => { - // check for and catch errors - try { - if (err) throw err; - let output = execSync('npm run asbuild', { cwd: __dirname }); - console.log("Compile result: " + output); + try { + const newOut = path.resolve(outPath, newName); + + asc.main([ + inputPath, + "--extension", "asc", + "--binaryFile", newOut, + "--measure", + "--runtime", "full", + production ? "--optimize" : "--sourceMap" + ], (err) => { + //let output = execSync('npm run asbuild', { cwd: __dirname }); + if(err) throw err; // none? -> read and resolve optimized.wasm string - if (production) - resolve({ - normal: fs.readFileSync(optPath) - }); - else - resolve({ - normal: fs.readFileSync(untPath), - map: fs.readFileSync(untMap) - }); - } - catch (ex) { - console.warn(pluginName + " Compile Error!"); - console.error(ex); - } - }); + resolve(production ? { + normal: fs.readFileSync(newOut) + } : { + normal: fs.readFileSync(newOut), + map: fs.readFileSync(newOut + ".map") + }); + + // TODO cleanup generated files + }); + } + catch (ex) { + console.warn("[" + pluginName + "] Compile Error!"); + console.error(ex); + } }); } @@ -110,7 +109,7 @@ class WascPlugin { }, async () => { if (addedOnce) return; addedOnce = true; - console.log('This is an experimental plugin!'); + console.log("[" + pluginName + "] Gathering Infos...."); // add static files from folder const rPath = path.resolve(__dirname, this.options.relpath); @@ -123,13 +122,14 @@ class WascPlugin { // if regex match wasm name, compile if (sName.match(this.options.regexx)) { - console.info(`Compile ${this.options.production ? "prod" : "debug"} wasm: ${sName}`); - compileWasm(rPath + sFile, this.options.production).then(res => { + console.info(`[${pluginName}] Compile ${this.options.production ? "prod" : "debug"} wasm: ${sName}`); + const newName = sName.replace(/\.[^/.]+$/, ""); + + await compileWasm(rPath + sFile, newName, this.options.production).then(({ normal, map }) => { // keep ".wasm" and remove ".ts" part of name - const newName = sName.replace(/\.[^/.]+$/, ""); - console.info("Success! File: " + newName); - if (res.normal) compilation.emitAsset(newName, new RawSource(res.normal)); - if (res.map) compilation.emitAsset(newName + ".map", new RawSource(res.map)); + console.info("[" + pluginName + "] Success: " + newName); + if (normal) compilation.emitAsset(newName, new RawSource(normal)); + if (map) compilation.emitAsset(newName + ".map", new RawSource(map)); }); } } diff --git a/src/wasc-builder/assembly/index.asc b/src/wasc-builder/assembly/index.asc deleted file mode 100644 index dc25421..0000000 --- a/src/wasc-builder/assembly/index.asc +++ /dev/null @@ -1,252 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * Wallaper Engine Audio Supplier worker. - */ - -const DAT_LEN = 128; -const HLF_LEN = 64; -const QRT_LEN = 32; - -const pinkNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, - 0.5452348949654, 0.50723325864167, 0.4677726234682, 0.44204182748767, 0.41956517802157, - 0.41517375040002, 0.41312118577934, 0.40618363960446, 0.39913707474975, 0.38207008614508, - 0.38329789106488, 0.37472136606245, 0.36586428412968, 0.37603017335105, 0.39762590761573, - 0.39391828858591, 0.37930603769622, 0.39433365764563, 0.38511504613859, 0.39082579241834, - 0.3811852720504, 0.40231453727161, 0.40244151133175, 0.39965366884521, 0.39761103827545, - 0.51136400422212, 0.66151212038954, 0.66312205226679, 0.7416276690995, 0.74614971301133, - 0.84797007577483, 0.8573583910469, 0.96382997811663, 0.99819377577185, 1.0628692615814, - 1.1059083969751, 1.1819808497335, 1.257092297208, 1.3226521464753, 1.3735992532905, - 1.4953223705889, 1.5310064942373, 1.6193923584808, 1.7094805527135, 1.7706604552218, - 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, - 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, - 2.4043566176474, 2.4280476777842, 2.3917477397336, 2.4032522546622, 2.3614180150678]; - -// correct pink noise for first and second half -function correctPinkNoise(data: Float64Array): void { - var correct = new Float64Array(DAT_LEN); - for (var i = 0; i < HLF_LEN; i++) { - correct[i] = data[i] / pinkNoise[i]; - correct[HLF_LEN + i] = data[HLF_LEN + i] / pinkNoise[i]; - } - data.set(correct); -} - -// merge first and second half into full range -function stereoToMono(data: Float64Array): void { - var mono = new Float64Array(DAT_LEN); - var mIdx = 0; - for (var i = 0; i < HLF_LEN; i++) { - mono[mIdx++] = data[i]; - mono[mIdx++] = data[HLF_LEN + i]; - } - data.set(mono); -} - -// switch front with back in first half -function invertFirst(data: Float64Array): void { - for (var i = 0; i < QRT_LEN; i++) { - var a = data[i]; - data[i] = data[HLF_LEN - i]; - data[HLF_LEN - i] = a; - } -} - -// switch front with back in second half -function invertSecond(data: Float64Array): void { - for (var i = 0; i < QRT_LEN; i++) { - var b = data[HLF_LEN + i]; - data[HLF_LEN + i] = data[DAT_LEN - i]; - data[DAT_LEN - i] = b; - } -} - -// switch front with back in full range -function invertAll(data: Float64Array): void { - for (var i = 0; i < HLF_LEN; i++) { - var a = data[i]; - data[i] = data[DAT_LEN - i]; - data[DAT_LEN - i] = a; - } -} - -// filter peaks for full range -function peakFilter(array: Float64Array, amount: f64): void { - var oldMax: f64 = 0; - var newMax: f64 = 0; - var newArray = new Float64Array(DAT_LEN); - var i = 0; - // pow this shit - for (; i < DAT_LEN; i++) { - if (array[i] > oldMax) oldMax = array[i]; - newArray[i] = Math.pow(array[i] * amount, amount) as f64; - if (newArray[i] > newMax) newMax = newArray[i]; - } - // re-scale & apply - var divide: f64 = newMax / oldMax; - for (i = 0; i < DAT_LEN; i++) - array[i] = newArray[i] / divide; -} - -// smooth values for full range -function smoothArray(array: Float64Array, steps: i32): void { - var newArray = new Float64Array(DAT_LEN); - // make smoothed array - for (var outer = 0; outer < DAT_LEN; outer++) { - var sum: f64 = 0; - for (var inner = outer - steps; inner <= outer + steps; inner++) { - var idx = inner; - if (idx < 0) idx += DAT_LEN; - sum += array[idx % DAT_LEN]; - } - newArray[outer] = sum / (((steps * 2) + 1) as f64); - } - array.set(newArray); -} - -// function will apply setting-defined data smoothing -function applyValueLeveling(curr: Float64Array, prev: Float64Array, inc: f64, dec: f64): void { - for (var i = 0; i < DAT_LEN; i++) { - var diff: f64 = curr[i] - prev[i]; - var mlt: f64 = 100 - (diff > 0 ? inc : dec); - curr[i] -= diff * mlt / 100; - } -} - -// this will hold the current processed audio data -// either: B-H | H-B | HL-BL-BR-HR | BL-HL-HR-BR -// where ( B=bass, H=high, L=left, R=right ) -// in range > 0.0 and ~< 1.5 -export const audioData = new Float64Array(DAT_LEN); - -// this will hold the current processed properties: -export const audioProps = new Float64Array(10); -enum Props { - bass = 0, - mids = 1, - highs = 2, - sum = 3, - min = 4, - max = 5, - average = 6, - range = 7, - silent = 8, - intensity = 9 -} - - -// this will hold the current processing settings -export const audioSettings = new Float64Array(8); -enum Sett { - equalize = 0, - mono_audio = 1, - audio_direction = 2, - peak_filter = 3, - value_smoothing = 4, - audio_increase = 5, - audio_decrease = 6, - minimum_volume = 7 -} - -// check helper -function isOn(a: f64): boolean { - return a > 0; -} - -// this will update and process new data -export function update(newData: Float64Array): void { - - // fix pink noise? - if (isOn(audioSettings[Sett.equalize])) - correctPinkNoise(newData); - - // write botch channels to mono - if (isOn(audioSettings[Sett.mono_audio])) - stereoToMono(newData); - - if (isOn(audioSettings[Sett.audio_direction])) { - // flipped high & low mapping - if (isOn(audioSettings[Sett.mono_audio])) - // flip whole range - invertAll(newData); - else { - // only flip first half of stereo - invertFirst(newData); - } - - } - else { - // normal high & low mapping - if (isOn(audioSettings[Sett.mono_audio])) { - // only flip the second half of the data - invertSecond(newData); - } - } - - // process peaks? - if (isOn(audioSettings[Sett.peak_filter])) - peakFilter(newData, audioSettings[Sett.peak_filter] + 1); - - // smooth data? - if (isOn(audioSettings[Sett.value_smoothing])) - smoothArray(newData, audioSettings[Sett.value_smoothing] as i32); - - // process with last data? - if (audioData) - applyValueLeveling(newData, audioData, - audioSettings[Sett.audio_increase], - audioSettings[Sett.audio_decrease]); - - // process current frequency data and previous if given - var sum: f64 = 0, - min: f64 = 1, - max: f64 = 0, - bass: f64 = 0, - mids: f64 = 0, - peaks: f64 = 0; - - for (var indx = 0; indx < DAT_LEN; indx++) { - // parse current freq value - var idata: f64 = newData[indx]; - // process min max value - if (idata < min) min = idata; - if (idata > max) max = idata; - // process ranges - if (indx < (42 / 3)) bass += idata * audioProps[Props.bass]; - else if (indx > 69) peaks += idata * audioProps[Props.highs]; - else mids += idata * audioProps[Props.mids]; - // calc peak average - sum += idata; - } - - // calc average with previous entry - var average: f64 = sum / (DAT_LEN as f64); - var silent: f64 = (max < audioSettings[Sett.minimum_volume] / 1000) ? 0.9999 : 0.00; - var intensity: f64 = (bass * 8 - mids + peaks) / 6 / average; - var range: f64 = max - min; - - // Apply Data - audioData.set(newData); - audioProps.set([ - bass, - mids, - peaks, - sum, - min, - max, - average, - range, - silent, - intensity - ]); - - // clean - gc.collect(); -} - diff --git a/src/wasc-builder/index.js b/src/wasc-builder/index.js deleted file mode 100644 index 708043e..0000000 --- a/src/wasc-builder/index.js +++ /dev/null @@ -1,8 +0,0 @@ -const fs = require("fs"); -const loader = require("@assemblyscript/loader"); -const imports = { /* imports go here */ }; -const wasmModule = loader.instantiateSync(fs.readFileSync(__dirname + "/build/optimized.wasm"), imports); -//module.exports = wasmModule.exports; - - -var res = wasmModule.exports.update(new Float32Array(128)); \ No newline at end of file diff --git a/src/wasc-worker b/src/wasc-worker index e23fd12..4e21ab2 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit e23fd128135600f5ce722688e5511502d91eee17 +Subproject commit 4e21ab21c8a26cfabc0b166f16d51461258305f0 diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 04cc64e..21c029f 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -93,8 +93,11 @@ export class WEAS extends CComponent { // settings object public settings: WEASettings = new WEASettings(); + // create transfer buffer + private inBuff = new Float64Array(DAT_LEN); + // web assembly functions - private run: any = null; + private weasModule: any = null; constructor() { super(); @@ -102,7 +105,7 @@ export class WEAS extends CComponent { Ready.On(() => this.realInit()); } - private realInit() { + private async realInit() { // if wallpaper engine context given, listen if (!window['wallpaperRegisterAudioListener']) { Smallog.Info("'window.wallpaperRegisterAudioListener' not given!"); @@ -111,75 +114,60 @@ export class WEAS extends CComponent { var self = this; - wascModule('WEAS.wasm', {}, false) - .then(r => { - // save module context - console.log(r); - const { module, instance, exports, run } = r as any; - this.run = run; - // create transfer buffer - const inBuff = new Float64Array(DAT_LEN); - - // pass settings to module - this.updateSettings(); - - // register audio callback on module - window['wallpaperRegisterAudioListener'](async audioArray => { - // check proof - if (!audioArray) return; - if (audioArray.length != DAT_LEN) { - Smallog.Error("audioListener: received invalid audio data array. Length: " + audioArray.length); - return; - } - - // prepare data - const start = performance.now(); - inBuff.set(audioArray); - - // WRAP UPDATE IN RUN - this.run( - // isolated Function ran inside worker - ({ module, instance, importObject, params }) => { - const { exports } = instance; - const { data } = params[0]; - - //console.debug("Send Data to Worker: " + JSON.stringify(data)); - - // apply data to module memory - const transfer = importObject.__getFloat64ArrayView(exports.inputData); - transfer.set(data); - // actual audio data processing - exports.update(); - - // get updates Data & Properties - return { - data: importObject.__getFloat64ArrayView(exports.outputData), - props: importObject.__getFloat64ArrayView(exports.audioProps), - } - }, - // Data passed to worker - { - data: inBuff - }) - .then( - // worker result, back in main context - (result) => { - const { data, props } = result; - // apply actual last Data from worker - self.lastAudio = { - time: start, - data, - ...this.getProps(props) // @TODO make props to Props mapping - }; - - // print info - Smallog.Debug("Got Data from Worker! Time= " + (performance.now() - start) + ", Data= " + JSON.stringify(data)); - - } - ); - }); - - }).catch(ex => Smallog.Error(JSON.stringify(ex))); + this.weasModule = await wascModule('WEAS.wasm', {}, true); + const { run } = this.weasModule; + + + // pass settings to module + this.updateSettings(); + + // register audio callback on module + window['wallpaperRegisterAudioListener'](async audioArray => { + // check proof + if (!audioArray) return; + if (audioArray.length != DAT_LEN) { + Smallog.Error("audioListener: received invalid audio data array. Length: " + audioArray.length); + return; + } + + // prepare data + const start = performance.now(); + this.inBuff.set(audioArray); + + // WRAP IN isolated Function ran inside worker + run(({ module, instance, importObject, params }) => { + const { exports } = instance; + const { data } = params[0]; + + //console.debug("Send Data to Worker: " + JSON.stringify(data)); + + // apply data to module memory + const transfer = importObject.__getFloat64ArrayView(exports.inputData); + transfer.set(data); + // actual audio data processing + exports.update(); + + // get updates Data & Properties + return { + data: importObject.__getFloat64ArrayView(exports.outputData), + props: importObject.__getFloat64ArrayView(exports.audioProps), + } + }, { + // params passed to worker + data: this.inBuff + }).then((result) => { + // worker result, back in main context + const { data, props } = result; + // apply actual last Data from worker + self.lastAudio = { + time: start, + data, + ...this.getProps(props) + }; + // print info + Smallog.Debug("Got Data from Worker! Time= " + (performance.now() - start) + ", Data= " + JSON.stringify(data)); + }); + }); } private getProps(dProps: ArrayLike) { @@ -195,7 +183,8 @@ export class WEAS extends CComponent { // CAVEAT: only available after init and module load public async updateSettings() { - if (!this.run) return; + if (!this.weasModule) return; + const { run } = this.weasModule; var keys = Object.keys(Sett); keys = keys.slice(keys.length / 2); @@ -205,22 +194,19 @@ export class WEAS extends CComponent { sett[Sett[key]] = this.settings[key] || 0; } - // WRAP IN RUN - await this.run( - // isolated Function ran inside worker - ({ module, instance, importObject, params }) => { - const { exports } = instance; - const { data } = params[0]; + // WRAP IN isolated Function ran inside worker + await run(({ module, instance, importObject, params }) => { + const { exports } = instance; + const { data } = params[0]; - const transfer = importObject.__getFloat64ArrayView(exports.audioSettings); - transfer.set(data); + const transfer = importObject.__getFloat64ArrayView(exports.audioSettings); + transfer.set(data); - console.debug("Send Settings to Worker: " + JSON.stringify(data)); - }, + console.debug("Send Settings to Worker: " + JSON.stringify(data)); + }, { // Data passed to worker - { - data: sett - }); + data: sett + }); } public hasAudio() { diff --git a/src/weas/WEASWorker.ts b/src/weas/WEASWorker.ts deleted file mode 100644 index 791e767..0000000 --- a/src/weas/WEASWorker.ts +++ /dev/null @@ -1,199 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * Wallaper Engine Audio Supplier worker. - */ - -const pinkNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, - 0.5452348949654, 0.50723325864167, 0.4677726234682, 0.44204182748767, 0.41956517802157, - 0.41517375040002, 0.41312118577934, 0.40618363960446, 0.39913707474975, 0.38207008614508, - 0.38329789106488, 0.37472136606245, 0.36586428412968, 0.37603017335105, 0.39762590761573, - 0.39391828858591, 0.37930603769622, 0.39433365764563, 0.38511504613859, 0.39082579241834, - 0.3811852720504, 0.40231453727161, 0.40244151133175, 0.39965366884521, 0.39761103827545, - 0.51136400422212, 0.66151212038954, 0.66312205226679, 0.7416276690995, 0.74614971301133, - 0.84797007577483, 0.8573583910469, 0.96382997811663, 0.99819377577185, 1.0628692615814, - 1.1059083969751, 1.1819808497335, 1.257092297208, 1.3226521464753, 1.3735992532905, - 1.4953223705889, 1.5310064942373, 1.6193923584808, 1.7094805527135, 1.7706604552218, - 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, - 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, - 2.4043566176474, 2.4280476777842, 2.3917477397336, 2.4032522546622, 2.3614180150678]; - -class AudioProcessor { - - public runWithSettings(data, settings, lastData?) { - - // fix pink noise? - if (settings.equalize) this.correctPinkNoise(data); - - // write botch channels to mono - if (settings.mono_audio) this.stereoToMono(data); - - // normal high & low mapping - if (settings.audio_direction == 0) { - // only flip the second half of the data - if (!settings.mono_audio) this.invertSecond(data); - } - // flipped high & low mapping - else { - // flip whole range - if (settings.mono_audio) this.invertAll(data); - // only flip first half of stereo - else this.invertFirst(data); - } - - // process peaks? - if (settings.peak_filter > 0) this.peakFilter(data, settings.peak_filter + 1); - - // smooth data? - if (settings.value_smoothing > 0) this.smoothArray(data, settings.value_smoothing); - - // process with last data? - if (lastData && lastData.data) this.applyValueLeveling(data, lastData.data, settings); - } - - // correct pink noise for first and second half - private correctPinkNoise(data) { - var correct = []; - for (var i = 0; i < 64; i++) { - correct[i] = data[i] / pinkNoise[i]; - correct[64 + i] = data[64 + i] / pinkNoise[i]; - } - return correct; - } - - // merge first and second half into full range - private stereoToMono(data) { - var mono = []; - var mIdx = 0; - for (var i = 0; i < 64; i++) { - mono[mIdx++] = data[i]; - mono[mIdx++] = data[64 + i]; - } - return mono; - } - - // switch front with back in first half - private invertFirst(data) { - for (var i = 0; i < 32; i++) { - var a = data[i]; - data[i] = data[64 - i]; - data[64 - i] = a; - } - } - - // switch front with back in second half - private invertSecond(data) { - for (var i = 0; i < 32; i++) { - var b = data[64 + i]; - data[64 + i] = data[128 - i]; - data[128 - i] = b; - } - } - - // switch front with back in full range - private invertAll(data) { - for (var i = 0; i < 64; i++) { - var a = data[i]; - data[i] = data[128 - i]; - data[128 - i] = a; - } - } - - // filter peaks for full range - private peakFilter(array, amount) { - var oldMax = 0; - var newMax = 0; - var newArray = new Float32Array(array.length); - // pow this shit - for (var i = 0; i < array.length; i++) { - if (array[i] > oldMax) oldMax = array[i]; - newArray[i] = Math.pow(array[i] * amount, amount); - if (newArray[i] > newMax) newMax = newArray[i]; - } - // re-scale & apply - var divide = newMax / oldMax; - for (var i = 0; i < array.length; i++) - array[i] = newArray[i] / divide; - } - - // smooth values for full range - private smoothArray(array, smoothing) { - var newArray = new Float32Array(array.length); - // make smoothed array - for (var i = 0; i < array.length; i++) { - var sum = 0; - for (var index = i - smoothing; index <= i + smoothing; index++) - sum += array[index < 0 ? index + array.length : index % array.length]; - newArray[i] = sum / ((smoothing * 2) + 1); - } - // copy new data to old array - for (var i = 0; i < array.length; i++) - array[i] = newArray[i]; - } - - // function will apply setting-defined data smoothing - private applyValueLeveling(curr, prev, sett) { - for (var i = 0; i < curr.length; i++) { - var diff = curr[i] - prev[i]; - var mlt = 100 - (diff > 0 ? sett.audio_increase : sett.audio_decrease); - curr[i] -= diff * mlt / 100; - } - } -} - -// make audioProcessor -const proc = new AudioProcessor(); - -// get the typed object -const ctx: Worker = self as any; - -ctx.addEventListener("message", (e) => { - let eventData = e.data; - // can be null - let data = new Float32Array(eventData.audio); - let lastData = eventData.last; - let settings = eventData.settings; - - proc.runWithSettings(data, settings, lastData); - - // process current frequency data and previous if given - var sum = 0, min = 1, max = 0, bass = 0, mids = 0, peaks = 0; - for (var i = 0; i < data.length; i++) { - // parse current freq value - var idata = data[i]; - // fix null values - if (idata == null || isNaN(idata)) data[i] = idata = 0.0; - // process min max value - if (idata < min) min = idata; - if (idata > max) max = idata; - // process ranges - if (i < 10) bass += idata * settings.bass_multiplier; - else if (i > 70) peaks += idata * settings.treble_multiplier; - else mids += idata * settings.mids_multiplier; - // calc peak average - sum += idata; - } - // calc average with previous entry - var average = sum / data.length; - // done - ctx.postMessage({ - bass: bass, - mids: mids, - peaks: peaks, - sum: sum, - min: min, - max: max, - average: average, - range: max - min, - silent: (max < settings.minimum_volume / 1000), - intensity: (bass * 8 - mids + peaks) / 6 / average, - time: eventData.time, - data: data.buffer, - }, [data.buffer]); -}); From 6dcc873afcf4c3a00ac56694a6289720f25e7164 Mon Sep 17 00:00:00 2001 From: hexxone Date: Sun, 10 Jan 2021 18:36:02 +0100 Subject: [PATCH 19/76] promises instead callbacks, rm unncessary stuff --- src/Ready.ts | 17 +- src/ReloadHelper.ts | 2 +- src/WEICUE.ts | 7 +- src/WEWWA.ts | 6 +- src/WarnHelper.ts | 22 +- src/wasc-builder/.gitignore | 3 + .../{WascPlugin.js => WascBuilderPlugin.js} | 45 +- src/wasc-builder/asconfig.json | 20 - src/wasc-builder/assembly/index.ts | 262 - src/wasc-builder/assembly/tsconfig.json | 6 - src/wasc-builder/build/optimized.wat | 3715 ----------- src/wasc-builder/build/untouched.wat | 5794 ----------------- src/wasc-builder/package.json | 7 +- src/wasc-builder/tests/index.js | 4 - src/weas/WEAS.ts | 2 +- src/worker-loader.d.ts | 4 + 16 files changed, 70 insertions(+), 9846 deletions(-) create mode 100644 src/wasc-builder/.gitignore rename src/wasc-builder/{WascPlugin.js => WascBuilderPlugin.js} (76%) delete mode 100644 src/wasc-builder/asconfig.json delete mode 100644 src/wasc-builder/assembly/index.ts delete mode 100644 src/wasc-builder/assembly/tsconfig.json delete mode 100644 src/wasc-builder/build/optimized.wat delete mode 100644 src/wasc-builder/build/untouched.wat delete mode 100644 src/wasc-builder/tests/index.js diff --git a/src/Ready.ts b/src/Ready.ts index 4ff1524..28e65db 100644 --- a/src/Ready.ts +++ b/src/Ready.ts @@ -1,5 +1,6 @@ /** - * @author D.Thiele @https://hexx.one + * @author D.Thiele + * @url https://hexx.one * * @license * Copyright (c) 2020 D.Thiele All rights reserved. @@ -10,14 +11,12 @@ * Shorthand Document ready wrapper */ -export module Ready { - export function On(fn: any) { - if (typeof fn !== 'function') - return false; +export function Ready() { + return new Promise(resolve => { // If document is already loaded, run method if (document.readyState === 'interactive' || document.readyState === 'complete') - return fn(); + resolve(true); // Otherwise, wait until document is loaded - document.addEventListener('DOMContentLoaded', fn, false); - }; -} + document.addEventListener('DOMContentLoaded', resolve, false); + }); +} \ No newline at end of file diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index bdeb672..c3c0be3 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -9,7 +9,7 @@ export class ReloadHelper { waitSeconds = 3; constructor() { - Ready.On(() => { + Ready().then(() => { this.injectCSS(); this.injectHTML(); }); diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 60fc163..0c8c9e6 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -68,7 +68,7 @@ export class WEICUE extends CComponent { if (name === 'led') this.isAvailable = true; } } - Ready.On(() => { + Ready().then(() => { // inject helpers this.injectCSS(); this.injectHTML(); @@ -76,7 +76,7 @@ export class WEICUE extends CComponent { }); } - injectCSS() { + private injectCSS() { var st = document.createElement("style"); st.innerHTML = ` #icueholder { @@ -109,11 +109,12 @@ export class WEICUE extends CComponent { document.head.append(st); } - injectHTML() { + private injectHTML() { var outer = document.createElement("div"); outer.id = "icueholder"; var imgg = document.createElement("img"); imgg.id = "icuelogo"; + // @TODO remove this abomination imgg.setAttribute("src", ` data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAABmJLR0QA/wD9AP+jOXP9AAAACXBIWXMAAC4jAAAuIwF4pT92AAAgAElEQ VR42u2dd3gU1frHP2e2ZNMrCSGhRZp0EBARVEREkY50URAQlCbivV7Fgg3bpYOCBRBU5IqoWFEQBJEOIihKTSghCSE92T7n98dmEX8iyW4myQb2+zw8msDMnDnnfOct diff --git a/src/WEWWA.ts b/src/WEWWA.ts index b02ac77..1fa61e1 100644 --- a/src/WEWWA.ts +++ b/src/WEWWA.ts @@ -79,8 +79,8 @@ export class WEWWA { } // intialize when ready - Ready.On(() => { - if(CC) {} + Ready().then(() => { + if(CC) { /* This tells the compiler to include CookieConsent at this point. */ } const cc = window['cookieconsent'].initialise({ palette: { popup: { background: "#000" }, @@ -612,8 +612,6 @@ export class WEWWA { } } - // TODO REMVOE ? - // will load the given file and return it as dataURL. // this way we can easily store whole files in the configuration & localStorage. // its not safe that this works with something else than image files. diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 0535c0a..4a59fe8 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -23,7 +23,7 @@ export class WarnHelper { waitSeconds = 10; constructor() { - Ready.On(() => { + Ready().then(() => { this.injectCSS(); this.injectHTML(); }); @@ -55,14 +55,16 @@ export class WarnHelper { document.body.append(outer); } - public Show(callback) { - // show it - $("#triggerwarn").addClass("show"); - // wait some time - setTimeout(() => { - // hide it & wait again - $("#triggerwarn").removeClass("show"); - setTimeout(callback, this.animationSeconds * 1000); - }, this.waitSeconds * 1000); + public Show() { + return new Promise(resolve => { + // show it + $("#triggerwarn").addClass("show"); + // wait some time + setTimeout(() => { + // hide it & wait again + $("#triggerwarn").removeClass("show"); + setTimeout(resolve, this.animationSeconds * 1000); + }, this.waitSeconds * 1000); + }); } }; \ No newline at end of file diff --git a/src/wasc-builder/.gitignore b/src/wasc-builder/.gitignore new file mode 100644 index 0000000..ef24d8a --- /dev/null +++ b/src/wasc-builder/.gitignore @@ -0,0 +1,3 @@ +*.wat + +./build/* \ No newline at end of file diff --git a/src/wasc-builder/WascPlugin.js b/src/wasc-builder/WascBuilderPlugin.js similarity index 76% rename from src/wasc-builder/WascPlugin.js rename to src/wasc-builder/WascBuilderPlugin.js index 527964c..be1481e 100644 --- a/src/wasc-builder/WascPlugin.js +++ b/src/wasc-builder/WascBuilderPlugin.js @@ -27,13 +27,16 @@ const schema = { type: 'object', properties: { production: { - typew: 'bool' + type: 'boolean' }, relpath: { type: 'string' }, regexx: { type: 'object' + }, + cleanup: { + type: 'boolean' } } }; @@ -68,16 +71,14 @@ function compileWasm(inputPath, newName, production) { production ? "--optimize" : "--sourceMap" ], (err) => { //let output = execSync('npm run asbuild', { cwd: __dirname }); - if(err) throw err; + if (err) throw err; // none? -> read and resolve optimized.wasm string resolve(production ? { normal: fs.readFileSync(newOut) } : { - normal: fs.readFileSync(newOut), - map: fs.readFileSync(newOut + ".map") - }); - - // TODO cleanup generated files + normal: fs.readFileSync(newOut), + map: fs.readFileSync(newOut + ".map") + }); }); } catch (ex) { @@ -85,11 +86,29 @@ function compileWasm(inputPath, newName, production) { console.error(ex); } }); +} +// delete all files in the output dir +function CleanUp() { + console.info("[" + pluginName + "] Cleaning..."); + return new Promise(resolve => { + fs.readdir(outPath, (err, files) => { + if (err) throw err; + Promise.all(files.map(file => { + return new Promise(res => { + fs.unlink(path.join(outPath, file), err => { + if (err) throw err; + console.info("[" + pluginName + "] delete: "+ file); + res(); + }); + }) + })).then(resolve); + }); + }); } // actual webpack plugin -class WascPlugin { +class WascBuilderPlugin { options = {}; @@ -123,21 +142,25 @@ class WascPlugin { // if regex match wasm name, compile if (sName.match(this.options.regexx)) { console.info(`[${pluginName}] Compile ${this.options.production ? "prod" : "debug"} wasm: ${sName}`); + // keep ".wasm" and remove ".ts" part of name const newName = sName.replace(/\.[^/.]+$/, ""); await compileWasm(rPath + sFile, newName, this.options.production).then(({ normal, map }) => { - // keep ".wasm" and remove ".ts" part of name console.info("[" + pluginName + "] Success: " + newName); + // emit files into compilation if (normal) compilation.emitAsset(newName, new RawSource(normal)); if (map) compilation.emitAsset(newName + ".map", new RawSource(map)); }); } } - console.info("[" + pluginName + "] successfull!"); + // finalize + if(this.options.cleanup) await CleanUp(); + + console.info("[" + pluginName + "] finished."); }) ); } } -module.exports = WascPlugin; \ No newline at end of file +module.exports = WascBuilderPlugin; \ No newline at end of file diff --git a/src/wasc-builder/asconfig.json b/src/wasc-builder/asconfig.json deleted file mode 100644 index 6405405..0000000 --- a/src/wasc-builder/asconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "targets": { - "debug": { - "binaryFile": "build/untouched.wasm", - "textFile": "build/untouched.wat", - "sourceMap": true, - "debug": true - }, - "release": { - "binaryFile": "build/optimized.wasm", - "textFile": "build/optimized.wat", - "sourceMap": true, - "optimizeLevel": 3, - "shrinkLevel": 1, - "converge": false, - "noAssert": false - } - }, - "options": {} -} \ No newline at end of file diff --git a/src/wasc-builder/assembly/index.ts b/src/wasc-builder/assembly/index.ts deleted file mode 100644 index fc748e6..0000000 --- a/src/wasc-builder/assembly/index.ts +++ /dev/null @@ -1,262 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * Wallaper Engine Audio Supplier worker. - */ - - - ////////////////////////// - // CUSTOM API - ////////////////////////// - -@external("env", "logf") -declare function logf(value: f64): void; - -@external("env", "logi") -declare function logi(value: u32): void; - -@external("env", "logU32Array") -declare function logU32Array(arr: Uint32Array): void; - -@external("env", "logF64Array") -declare function logF64Array(arr: Float64Array): void; - -export function allocF64Array(length: i32): Float64Array { - return new Float64Array(length); -} - -export function allocU32Array(length: i32): Uint32Array { - return new Uint32Array(length); -} - -@inline -function deallocArray(arr: T[]): void { - memory.free(changetype(arr.buffer_)); - memory.free(changetype(arr)); -} - - ////////////////////////// - // Main Program - ////////////////////////// - -const DAT_LEN: i32 = 128; -const HLF_LEN: i32 = 64; -const QRT_LEN: i32 = 32; - -const pNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, - 0.5452348949654, 0.50723325864167, 0.4677726234682, 0.44204182748767, 0.41956517802157, - 0.41517375040002, 0.41312118577934, 0.40618363960446, 0.39913707474975, 0.38207008614508, - 0.38329789106488, 0.37472136606245, 0.36586428412968, 0.37603017335105, 0.39762590761573, - 0.39391828858591, 0.37930603769622, 0.39433365764563, 0.38511504613859, 0.39082579241834, - 0.3811852720504, 0.40231453727161, 0.40244151133175, 0.39965366884521, 0.39761103827545, - 0.51136400422212, 0.66151212038954, 0.66312205226679, 0.7416276690995, 0.74614971301133, - 0.84797007577483, 0.8573583910469, 0.96382997811663, 0.99819377577185, 1.0628692615814, - 1.1059083969751, 1.1819808497335, 1.257092297208, 1.3226521464753, 1.3735992532905, - 1.4953223705889, 1.5310064942373, 1.6193923584808, 1.7094805527135, 1.7706604552218, - 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, - 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, - 2.4043566176474, 2.4280476777842, 2.3917477397336, 2.4032522546622, 2.3614180150678]; -const pinkNoise = new Float64Array(HLF_LEN); -pinkNoise.set(pNoise); - - -// correct pink noise for first and second half -function correctPinkNoise(data: Float64Array): void { - var correct = new Float64Array(DAT_LEN); - for (var i = 0; i < HLF_LEN; i++) { - correct[i] = data[i] / pinkNoise[i]; - correct[HLF_LEN + i] = data[HLF_LEN + i] / pinkNoise[i]; - } - data.set(correct); -} - -// merge first and second half into full range -function stereoToMono(data: Float64Array): void { - var mono = new Float64Array(DAT_LEN); - var mIdx = 0; - for (var i = 0; i < HLF_LEN; i++) { - mono[mIdx++] = data[i]; - mono[mIdx++] = data[HLF_LEN + i]; - } - data.set(mono); -} - -// switch front with back in first half -function invertFirst(data: Float64Array): void { - for (var i = 0; i < QRT_LEN; i++) { - var a = data[i]; - data[i] = data[HLF_LEN - 1 - i]; - data[HLF_LEN - 1 - i] = a; - } -} - -// switch front with back in second half -function invertSecond(data: Float64Array): void { - for (var i = 0; i < QRT_LEN; i++) { - var b = data[HLF_LEN + i]; - data[HLF_LEN + i] = data[DAT_LEN - 1 - i]; - data[DAT_LEN - 1 - i] = b; - } -} - -// switch front with back in full range -function invertAll(data: Float64Array): void { - for (var i = 0; i < HLF_LEN; i++) { - var a = data[i]; - data[i] = data[DAT_LEN - 1 - i]; - data[DAT_LEN - 1 - i] = a; - } -} - -// filter peaks for full range -function peakFilter(array: Float64Array, amount: f64): void { - var oldMax: f64 = 0; - var newMax: f64 = 0; - var newArray = new Float64Array(DAT_LEN); - var i = 0; - // pow this shit - for (; i < DAT_LEN; i++) { - if (array[i] > oldMax) oldMax = array[i]; - newArray[i] = Math.pow(array[i] * amount, amount) as f64; - if (newArray[i] > newMax) newMax = newArray[i]; - } - // re-scale & apply - var divide: f64 = newMax / oldMax; - for (i = 0; i < DAT_LEN; i++) - array[i] = newArray[i] / divide; -} - -// smooth values for full range -function smoothArray(array: Float64Array, steps: i32): void { - var newArray = new Float64Array(DAT_LEN); - // make smoothed array - for (var outer = 0; outer < DAT_LEN; outer++) { - var sum: f64 = 0; - for (var inner = outer - steps; inner <= outer + steps; inner++) { - var idx = inner; - if (idx < 0) idx += DAT_LEN; - sum += array[idx % DAT_LEN]; - } - newArray[outer] = sum / (((steps * 2) + 1) as f64); - } - array.set(newArray); -} - -// function will apply setting-defined data smoothing -function applyValueLeveling(curr: Float64Array, prev: Float64Array, inc: f64, dec: f64): void { - for (var i = 0; i < DAT_LEN; i++) { - var diff: f64 = curr[i] - prev[i]; - var mlt: f64 = 100 - (diff > 0 ? inc : dec); - curr[i] -= diff * mlt / 100; - } -} - -// THEW INPUT VALUE TO WRITE TO -export const inputData = new Float64Array(DAT_LEN); -inputData.fill(0.0); - -// this will hold the current processed audio data -// either: B-H | H-B | HL-BL-BR-HR | BL-HL-HR-BR -// where ( B=bass, H=high, L=left, R=right ) -// in range > 0.0 and ~< 1.5 -export const outputData = new Float64Array(DAT_LEN); -outputData.fill(0.0); - -// this will hold the current processed properties: -export const audioProps = new Float64Array(10); -audioProps.fill(0.0); - -// this will hold the current processing settings -export const audioSettings = new Float64Array(11); -audioSettings.fill(0.0); - - -// check helper -function isOn(a: f64): boolean { - return a > 0; -} - -// this will update and process new data -export function update(): void { - - // fix pink noise? - if (isOn(audioSettings[0])) - correctPinkNoise(inputData); - - // write botch channels to mono - if (isOn(audioSettings[1])) - stereoToMono(inputData); - - if (isOn(audioSettings[2])) { - // flipped high & low mapping - if (isOn(audioSettings[1])) - // flip whole range - invertAll(inputData); - else { - // only flip first half of stereo - invertFirst(inputData); - } - } - else { - // normal high & low mapping - if (isOn(audioSettings[1])) { - // only flip the second half of the data - invertSecond(inputData); - } - } - - // process peaks? - if (isOn(audioSettings[3])) - peakFilter(inputData, audioSettings[3] + 1); - - // smooth data? - if (isOn(audioSettings[4])) - smoothArray(inputData, Math.floor(audioSettings[4]) as i32); - - //logF64Array(inputData); - //logF64Array(outputData); - - // process with last data - applyValueLeveling(inputData, outputData, - audioSettings[5], - audioSettings[6]); - - // process current frequency data and previous if given - var sum: f64 = 0, - min: f64 = 1, - max: f64 = 0, - bass: f64 = 0, - mids: f64 = 0, - peaks: f64 = 0; - - for (var indx = 0; indx < DAT_LEN; indx++) { - // parse current freq value - var idata: f64 = inputData[indx]; - // process min max value - if (idata < min) min = idata; - if (idata > max) max = idata; - // process ranges - if (indx < (42 / 3)) bass += idata * audioSettings[9]; - else if (indx > 69) peaks += idata * audioSettings[7]; - else mids += idata * audioSettings[8]; - // calc peak average - sum += idata; - } - - // calc average with previous entry - var average: f64 = sum / (DAT_LEN as f64); - var silent: f64 = (max < audioSettings[10] / 1000) ? 0.9999 : 0.00; - var intensity: f64 = (bass * 8 - mids + peaks) / 6 / average; - var range: f64 = max - min; - - // Apply Data - outputData.set(inputData); - audioProps.set([bass,mids,peaks,sum,min,max,average,range,silent,intensity]) - // DONE -} diff --git a/src/wasc-builder/assembly/tsconfig.json b/src/wasc-builder/assembly/tsconfig.json deleted file mode 100644 index e28fcf2..0000000 --- a/src/wasc-builder/assembly/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "assemblyscript/std/assembly.json", - "include": [ - "./**/*.ts" - ] -} \ No newline at end of file diff --git a/src/wasc-builder/build/optimized.wat b/src/wasc-builder/build/optimized.wat deleted file mode 100644 index 427b57e..0000000 --- a/src/wasc-builder/build/optimized.wat +++ /dev/null @@ -1,3715 +0,0 @@ -(module - (type $i32_=>_i32 (func (param i32) (result i32))) - (type $i32_i32_=>_none (func (param i32 i32))) - (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) - (type $none_=>_none (func)) - (type $i32_=>_none (func (param i32))) - (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) - (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) - (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) - (type $i32_i32_f64_=>_none (func (param i32 i32 f64))) - (type $i32_i32_=>_f64 (func (param i32 i32) (result f64))) - (type $f64_f64_=>_f64 (func (param f64 f64) (result f64))) - (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) - (memory $0 1) - (data (i32.const 1036) "(\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00(\00\00\00a\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e") - (data (i32.const 1100) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00p\00u\00r\00e\00.\00t\00s") - (data (i32.const 1164) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") - (data (i32.const 1229) "\02\00\00\01") - (data (i32.const 1245) "\02\00\00Z>v\e8\0b\d1\f2?yY\f9B0D\eb?\0d\94\96\8c\92\07\e6?Xd\18\d8\ddg\e4?\deuPs\90r\e1?G\e7)>A;\e0?\83\a4\f0\95\fc\ef\dd?\ef\82!\ceiJ\dc?\ea\'\89\e7\'\da\da?V\0c\08\ec4\92\da?\94?\8d\d7\93p\da?\b8^\11\aa\e9\fe\d9?P\f6\aa:v\8b\d9?.y1\17\d6s\d8?\aa\f6\af\e0\f3\87\d8?Tq\16So\fb\d7?\a0!\c7\07Rj\d7?\af\866\dc\e0\10\d8?\c4\1eP\ef\b3r\d9?\1fu\b1\0d\f55\d9?\af(\c5\d4\8cF\d8?\e03\d3<\c3<\d9?\b57\17\94\b9\a5\d8?\fb\ae7/J\03\d9?\da\17K\e9Ve\d8?1]\12y\85\bf\d9?b@n\n\9a\c1\d9?m\a9Z\fb\ec\93\d9?\83\fcz\91ur\d9?!\8aO\0b\18]\e0?\cea_w\1b+\e5?\a4\c0\f7\bcK8\e5?\fb\e9\12\f3i\bb\e7?\d9\b5\e9\\u\e0\e7?\af\10\ee#\92\"\eb?v\ecO\ddzo\eb?PJ]\f7\b1\d7\ee?\e8X\c0\124\f1\ef?\0b\a7\e62\83\01\f1?\d3\13\d6\00\cd\b1\f1?\a5\a8a\c0d\e9\f2?n\fd\08\d0\0c\1d\f4?\a4\87\11L\95)\f5?_\15\eb5C\fa\f5?\19\81j&\d7\ec\f7?\b2gk\aa\00\7f\f8?\d8\131\f6\07\e9\f9?\fc\d6\b0G\08Z\fb?;\f8\b7\0e\a0T\fc?\ddU\8ayQ\96\fd?\ff\ea\a7m\0e\c8\fe?U\12\b5\c1\ff\1c\00@*\d10\8e\0f\a1\00@^[I\c1\aaB\01@\0e\aa\dbH\d0\c1\01@pJ\ba\81\ca \02@\aa\a5\eb\c1\eb\90\02@m\t\11\01!\dc\02@\85\84\0d\e8[0\03@\ec\bb\85R\1f<\03@\f1\b1\c9B\a4l\03@z\80\93\a3L\"\03@\d2\7fnQ\dc9\03@$6\d7 /\e4\02@") - (data (i32.const 1772) "\10\00\00\00\01\00\00\00\00\00\00\00\03\00\00\00\10\00\00\00\e0\04\00\00\e0\04\00\00\00\02\00\00@") - (data (i32.const 1820) "\1c\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1c\00\00\00I\00n\00v\00a\00l\00i\00d\00 \00l\00e\00n\00g\00t\00h") - (data (i32.const 1868) "&\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00&\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00b\00u\00f\00f\00e\00r\00.\00t\00s") - (data (i32.const 1932) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e") - (data (i32.const 1996) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00~\00l\00i\00b\00/\00t\00y\00p\00e\00d\00a\00r\00r\00a\00y\00.\00t\00s") - (data (i32.const 2064) "\08\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\"\1a\00\00\00\00\00\00!\1a\00\00\02\00\00\00a\00\00\00\02\00\00\00!\01\00\00\02\00\00\00\"\t") - (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) - (global $assembly/index/pinkNoise (mut i32) (i32.const 0)) - (global $assembly/index/inputData (mut i32) (i32.const 0)) - (global $assembly/index/outputData (mut i32) (i32.const 0)) - (global $assembly/index/audioProps (mut i32) (i32.const 0)) - (global $assembly/index/audioSettings (mut i32) (i32.const 0)) - (global $~lib/rt/__rtti_base i32 (i32.const 2064)) - (export "memory" (memory $0)) - (export "__new" (func $~lib/rt/pure/__new)) - (export "__renew" (func $~lib/rt/pure/__renew)) - (export "__retain" (func $~lib/rt/pure/__retain)) - (export "__release" (func $~lib/rt/pure/__release)) - (export "__rtti_base" (global $~lib/rt/__rtti_base)) - (export "allocF64Array" (func $assembly/index/allocF64Array)) - (export "allocU32Array" (func $assembly/index/allocU32Array)) - (export "inputData" (global $assembly/index/inputData)) - (export "outputData" (global $assembly/index/outputData)) - (export "audioProps" (global $assembly/index/audioProps)) - (export "audioSettings" (global $assembly/index/audioSettings)) - (export "update" (func $assembly/index/update)) - (start $~start) - (func $~lib/rt/tlsf/removeBlock (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - local.get $1 - i32.load - local.tee $2 - i32.const 1 - i32.and - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 272 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $2 - i32.const -4 - i32.and - local.tee $2 - i32.const 1073741820 - i32.lt_u - i32.const 0 - local.get $2 - i32.const 12 - i32.ge_u - select - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 274 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $2 - i32.const 256 - i32.lt_u - if - local.get $2 - i32.const 4 - i32.shr_u - local.set $2 - else - local.get $2 - i32.const 31 - local.get $2 - i32.clz - i32.sub - local.tee $3 - i32.const 4 - i32.sub - i32.shr_u - i32.const 16 - i32.xor - local.set $2 - local.get $3 - i32.const 7 - i32.sub - local.set $3 - end - local.get $2 - i32.const 16 - i32.lt_u - i32.const 0 - local.get $3 - i32.const 23 - i32.lt_u - select - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 287 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.load offset=8 - local.set $4 - local.get $1 - i32.load offset=4 - local.tee $5 - if - local.get $5 - local.get $4 - i32.store offset=8 - end - local.get $4 - if - local.get $4 - local.get $5 - i32.store offset=4 - end - local.get $1 - local.get $0 - local.get $2 - local.get $3 - i32.const 4 - i32.shl - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - i32.eq - if - local.get $0 - local.get $2 - local.get $3 - i32.const 4 - i32.shl - i32.add - i32.const 2 - i32.shl - i32.add - local.get $4 - i32.store offset=96 - local.get $4 - i32.eqz - if - local.get $0 - local.get $3 - i32.const 2 - i32.shl - i32.add - local.tee $4 - i32.load offset=4 - i32.const -2 - local.get $2 - i32.rotl - i32.and - local.set $1 - local.get $4 - local.get $1 - i32.store offset=4 - local.get $1 - i32.eqz - if - local.get $0 - local.get $0 - i32.load - i32.const -2 - local.get $3 - i32.rotl - i32.and - i32.store - end - end - end - ) - (func $~lib/rt/tlsf/insertBlock (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - local.get $1 - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 200 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.load - local.tee $4 - i32.const 1 - i32.and - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 202 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.const 4 - i32.add - local.get $1 - i32.load - i32.const -4 - i32.and - i32.add - local.tee $5 - i32.load - local.tee $2 - i32.const 1 - i32.and - if - local.get $4 - i32.const -4 - i32.and - i32.const 4 - i32.add - local.get $2 - i32.const -4 - i32.and - i32.add - local.tee $3 - i32.const 1073741820 - i32.lt_u - if - local.get $0 - local.get $5 - call $~lib/rt/tlsf/removeBlock - local.get $1 - local.get $3 - local.get $4 - i32.const 3 - i32.and - i32.or - local.tee $4 - i32.store - local.get $1 - i32.const 4 - i32.add - local.get $1 - i32.load - i32.const -4 - i32.and - i32.add - local.tee $5 - i32.load - local.set $2 - end - end - local.get $4 - i32.const 2 - i32.and - if - local.get $1 - i32.const 4 - i32.sub - i32.load - local.tee $3 - i32.load - local.tee $7 - i32.const 1 - i32.and - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 223 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $7 - i32.const -4 - i32.and - i32.const 4 - i32.add - local.get $4 - i32.const -4 - i32.and - i32.add - local.tee $8 - i32.const 1073741820 - i32.lt_u - if (result i32) - local.get $0 - local.get $3 - call $~lib/rt/tlsf/removeBlock - local.get $3 - local.get $8 - local.get $7 - i32.const 3 - i32.and - i32.or - local.tee $4 - i32.store - local.get $3 - else - local.get $1 - end - local.set $1 - end - local.get $5 - local.get $2 - i32.const 2 - i32.or - i32.store - local.get $4 - i32.const -4 - i32.and - local.tee $3 - i32.const 1073741820 - i32.lt_u - i32.const 0 - local.get $3 - i32.const 12 - i32.ge_u - select - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 238 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $3 - local.get $1 - i32.const 4 - i32.add - i32.add - local.get $5 - i32.ne - if - i32.const 0 - i32.const 1184 - i32.const 239 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $5 - i32.const 4 - i32.sub - local.get $1 - i32.store - local.get $3 - i32.const 256 - i32.lt_u - if - local.get $3 - i32.const 4 - i32.shr_u - local.set $3 - else - local.get $3 - i32.const 31 - local.get $3 - i32.clz - i32.sub - local.tee $4 - i32.const 4 - i32.sub - i32.shr_u - i32.const 16 - i32.xor - local.set $3 - local.get $4 - i32.const 7 - i32.sub - local.set $6 - end - local.get $3 - i32.const 16 - i32.lt_u - i32.const 0 - local.get $6 - i32.const 23 - i32.lt_u - select - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 255 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $3 - local.get $6 - i32.const 4 - i32.shl - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - local.set $4 - local.get $1 - i32.const 0 - i32.store offset=4 - local.get $1 - local.get $4 - i32.store offset=8 - local.get $4 - if - local.get $4 - local.get $1 - i32.store offset=4 - end - local.get $0 - local.get $3 - local.get $6 - i32.const 4 - i32.shl - i32.add - i32.const 2 - i32.shl - i32.add - local.get $1 - i32.store offset=96 - local.get $0 - local.get $0 - i32.load - i32.const 1 - local.get $6 - i32.shl - i32.or - i32.store - local.get $0 - local.get $6 - i32.const 2 - i32.shl - i32.add - local.tee $0 - local.get $0 - i32.load offset=4 - i32.const 1 - local.get $3 - i32.shl - i32.or - i32.store offset=4 - ) - (func $~lib/rt/tlsf/addMemory (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - local.get $1 - local.get $2 - i32.gt_u - if - i32.const 0 - i32.const 1184 - i32.const 380 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.const 19 - i32.add - i32.const -16 - i32.and - i32.const 4 - i32.sub - local.set $1 - local.get $2 - i32.const -16 - i32.and - local.get $0 - i32.load offset=1568 - local.tee $2 - if - local.get $1 - local.get $2 - i32.const 4 - i32.add - i32.lt_u - if - i32.const 0 - i32.const 1184 - i32.const 387 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $2 - local.get $1 - i32.const 16 - i32.sub - i32.eq - if - local.get $2 - i32.load - local.set $4 - local.get $1 - i32.const 16 - i32.sub - local.set $1 - end - else - local.get $1 - local.get $0 - i32.const 1572 - i32.add - i32.lt_u - if - i32.const 0 - i32.const 1184 - i32.const 400 - i32.const 5 - call $~lib/builtins/abort - unreachable - end - end - local.get $1 - i32.sub - local.tee $2 - i32.const 20 - i32.lt_u - if - return - end - local.get $1 - local.get $4 - i32.const 2 - i32.and - local.get $2 - i32.const 8 - i32.sub - local.tee $2 - i32.const 1 - i32.or - i32.or - i32.store - local.get $1 - i32.const 0 - i32.store offset=4 - local.get $1 - i32.const 0 - i32.store offset=8 - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.add - local.tee $2 - i32.const 2 - i32.store - local.get $0 - local.get $2 - i32.store offset=1568 - local.get $0 - local.get $1 - call $~lib/rt/tlsf/insertBlock - ) - (func $~lib/rt/tlsf/initialize - (local $0 i32) - (local $1 i32) - memory.size - local.tee $0 - i32.const 1 - i32.lt_s - if (result i32) - i32.const 1 - local.get $0 - i32.sub - memory.grow - i32.const 0 - i32.lt_s - else - i32.const 0 - end - if - unreachable - end - i32.const 2144 - i32.const 0 - i32.store - i32.const 3712 - i32.const 0 - i32.store - loop $for-loop|0 - local.get $1 - i32.const 23 - i32.lt_u - if - local.get $1 - i32.const 2 - i32.shl - i32.const 2144 - i32.add - i32.const 0 - i32.store offset=4 - i32.const 0 - local.set $0 - loop $for-loop|1 - local.get $0 - i32.const 16 - i32.lt_u - if - local.get $0 - local.get $1 - i32.const 4 - i32.shl - i32.add - i32.const 2 - i32.shl - i32.const 2144 - i32.add - i32.const 0 - i32.store offset=96 - local.get $0 - i32.const 1 - i32.add - local.set $0 - br $for-loop|1 - end - end - local.get $1 - i32.const 1 - i32.add - local.set $1 - br $for-loop|0 - end - end - i32.const 2144 - i32.const 3716 - memory.size - i32.const 16 - i32.shl - call $~lib/rt/tlsf/addMemory - i32.const 2144 - global.set $~lib/rt/tlsf/ROOT - ) - (func $~lib/rt/tlsf/prepareSize (param $0 i32) (result i32) - local.get $0 - i32.const 1073741820 - i32.ge_u - if - i32.const 1056 - i32.const 1184 - i32.const 461 - i32.const 30 - call $~lib/builtins/abort - unreachable - end - i32.const 12 - local.get $0 - i32.const 19 - i32.add - i32.const -16 - i32.and - i32.const 4 - i32.sub - local.get $0 - i32.const 12 - i32.le_u - select - ) - (func $~lib/rt/tlsf/searchBlock (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - local.get $1 - i32.const 256 - i32.lt_u - if - local.get $1 - i32.const 4 - i32.shr_u - local.set $1 - else - i32.const 31 - local.get $1 - i32.const 1 - i32.const 27 - local.get $1 - i32.clz - i32.sub - i32.shl - i32.add - i32.const 1 - i32.sub - local.get $1 - local.get $1 - i32.const 536870910 - i32.lt_u - select - local.tee $1 - i32.clz - i32.sub - local.set $2 - local.get $1 - local.get $2 - i32.const 4 - i32.sub - i32.shr_u - i32.const 16 - i32.xor - local.set $1 - local.get $2 - i32.const 7 - i32.sub - local.set $2 - end - local.get $1 - i32.const 16 - i32.lt_u - i32.const 0 - local.get $2 - i32.const 23 - i32.lt_u - select - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 333 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $2 - i32.const 2 - i32.shl - i32.add - i32.load offset=4 - i32.const -1 - local.get $1 - i32.shl - i32.and - local.tee $1 - if (result i32) - local.get $0 - local.get $1 - i32.ctz - local.get $2 - i32.const 4 - i32.shl - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - else - local.get $0 - i32.load - i32.const -1 - local.get $2 - i32.const 1 - i32.add - i32.shl - i32.and - local.tee $1 - if (result i32) - local.get $0 - local.get $1 - i32.ctz - local.tee $1 - i32.const 2 - i32.shl - i32.add - i32.load offset=4 - local.tee $2 - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 346 - i32.const 18 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $2 - i32.ctz - local.get $1 - i32.const 4 - i32.shl - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - else - i32.const 0 - end - end - ) - (func $~lib/rt/tlsf/prepareBlock (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - local.get $1 - i32.load - local.set $3 - local.get $2 - i32.const 4 - i32.add - i32.const 15 - i32.and - if - i32.const 0 - i32.const 1184 - i32.const 360 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $3 - i32.const -4 - i32.and - local.get $2 - i32.sub - local.tee $4 - i32.const 16 - i32.ge_u - if - local.get $1 - local.get $2 - local.get $3 - i32.const 2 - i32.and - i32.or - i32.store - local.get $2 - local.get $1 - i32.const 4 - i32.add - i32.add - local.tee $1 - local.get $4 - i32.const 4 - i32.sub - i32.const 1 - i32.or - i32.store - local.get $0 - local.get $1 - call $~lib/rt/tlsf/insertBlock - else - local.get $1 - local.get $3 - i32.const -2 - i32.and - i32.store - local.get $1 - i32.const 4 - i32.add - local.tee $0 - local.get $1 - i32.load - i32.const -4 - i32.and - i32.add - local.get $0 - local.get $1 - i32.load - i32.const -4 - i32.and - i32.add - i32.load - i32.const -3 - i32.and - i32.store - end - ) - (func $~lib/rt/tlsf/allocateBlock (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - local.get $0 - local.get $1 - call $~lib/rt/tlsf/prepareSize - local.tee $2 - call $~lib/rt/tlsf/searchBlock - local.tee $1 - i32.eqz - if - i32.const 4 - memory.size - local.tee $1 - i32.const 16 - i32.shl - i32.const 4 - i32.sub - local.get $0 - i32.load offset=1568 - i32.ne - i32.shl - local.get $2 - i32.const 1 - i32.const 27 - local.get $2 - i32.clz - i32.sub - i32.shl - i32.const 1 - i32.sub - i32.add - local.get $2 - local.get $2 - i32.const 536870910 - i32.lt_u - select - i32.add - i32.const 65535 - i32.add - i32.const -65536 - i32.and - i32.const 16 - i32.shr_u - local.set $3 - local.get $1 - local.get $3 - local.get $1 - local.get $3 - i32.gt_s - select - memory.grow - i32.const 0 - i32.lt_s - if - local.get $3 - memory.grow - i32.const 0 - i32.lt_s - if - unreachable - end - end - local.get $0 - local.get $1 - i32.const 16 - i32.shl - memory.size - i32.const 16 - i32.shl - call $~lib/rt/tlsf/addMemory - local.get $0 - local.get $2 - call $~lib/rt/tlsf/searchBlock - local.tee $1 - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 498 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - end - local.get $2 - local.get $1 - i32.load - i32.const -4 - i32.and - i32.gt_u - if - i32.const 0 - i32.const 1184 - i32.const 500 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $1 - call $~lib/rt/tlsf/removeBlock - local.get $0 - local.get $1 - local.get $2 - call $~lib/rt/tlsf/prepareBlock - local.get $1 - ) - (func $~lib/rt/pure/__new (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - local.get $0 - i32.const 1073741804 - i32.gt_u - if - i32.const 1056 - i32.const 1120 - i32.const 275 - i32.const 30 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.const 16 - i32.add - local.set $2 - global.get $~lib/rt/tlsf/ROOT - i32.eqz - if - call $~lib/rt/tlsf/initialize - end - global.get $~lib/rt/tlsf/ROOT - local.get $2 - call $~lib/rt/tlsf/allocateBlock - i32.const 4 - i32.add - local.tee $3 - i32.const 4 - i32.sub - local.tee $2 - i32.const 0 - i32.store offset=4 - local.get $2 - i32.const 0 - i32.store offset=8 - local.get $2 - local.get $1 - i32.store offset=12 - local.get $2 - local.get $0 - i32.store offset=16 - local.get $3 - i32.const 16 - i32.add - ) - (func $~lib/rt/tlsf/checkUsedBlock (param $0 i32) (result i32) - (local $1 i32) - local.get $0 - i32.const 4 - i32.sub - local.set $1 - local.get $0 - i32.const 15 - i32.and - i32.eqz - i32.const 0 - local.get $0 - select - if (result i32) - local.get $1 - i32.load - i32.const 1 - i32.and - i32.eqz - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 1184 - i32.const 563 - i32.const 3 - call $~lib/builtins/abort - unreachable - end - local.get $1 - ) - (func $~lib/memory/memory.copy (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - block $~lib/util/memory/memmove|inlined.0 - local.get $2 - local.set $4 - local.get $0 - local.get $1 - i32.eq - br_if $~lib/util/memory/memmove|inlined.0 - local.get $0 - local.get $1 - i32.lt_u - if - local.get $1 - i32.const 7 - i32.and - local.get $0 - i32.const 7 - i32.and - i32.eq - if - loop $while-continue|0 - local.get $0 - i32.const 7 - i32.and - if - local.get $4 - i32.eqz - br_if $~lib/util/memory/memmove|inlined.0 - local.get $4 - i32.const 1 - i32.sub - local.set $4 - local.get $0 - local.tee $2 - i32.const 1 - i32.add - local.set $0 - local.get $1 - local.tee $3 - i32.const 1 - i32.add - local.set $1 - local.get $2 - local.get $3 - i32.load8_u - i32.store8 - br $while-continue|0 - end - end - loop $while-continue|1 - local.get $4 - i32.const 8 - i32.ge_u - if - local.get $0 - local.get $1 - i64.load - i64.store - local.get $4 - i32.const 8 - i32.sub - local.set $4 - local.get $0 - i32.const 8 - i32.add - local.set $0 - local.get $1 - i32.const 8 - i32.add - local.set $1 - br $while-continue|1 - end - end - end - loop $while-continue|2 - local.get $4 - if - local.get $0 - local.tee $2 - i32.const 1 - i32.add - local.set $0 - local.get $1 - local.tee $3 - i32.const 1 - i32.add - local.set $1 - local.get $2 - local.get $3 - i32.load8_u - i32.store8 - local.get $4 - i32.const 1 - i32.sub - local.set $4 - br $while-continue|2 - end - end - else - local.get $1 - i32.const 7 - i32.and - local.get $0 - i32.const 7 - i32.and - i32.eq - if - loop $while-continue|3 - local.get $0 - local.get $4 - i32.add - i32.const 7 - i32.and - if - local.get $4 - i32.eqz - br_if $~lib/util/memory/memmove|inlined.0 - local.get $4 - i32.const 1 - i32.sub - local.tee $4 - local.get $0 - i32.add - local.get $1 - local.get $4 - i32.add - i32.load8_u - i32.store8 - br $while-continue|3 - end - end - loop $while-continue|4 - local.get $4 - i32.const 8 - i32.ge_u - if - local.get $4 - i32.const 8 - i32.sub - local.tee $4 - local.get $0 - i32.add - local.get $1 - local.get $4 - i32.add - i64.load - i64.store - br $while-continue|4 - end - end - end - loop $while-continue|5 - local.get $4 - if - local.get $4 - i32.const 1 - i32.sub - local.tee $4 - local.get $0 - i32.add - local.get $1 - local.get $4 - i32.add - i32.load8_u - i32.store8 - br $while-continue|5 - end - end - end - end - ) - (func $~lib/rt/tlsf/freeBlock (param $0 i32) (param $1 i32) - local.get $1 - local.get $1 - i32.load - i32.const 1 - i32.or - i32.store - local.get $0 - local.get $1 - call $~lib/rt/tlsf/insertBlock - ) - (func $~lib/rt/tlsf/moveBlock (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - local.get $0 - local.get $2 - call $~lib/rt/tlsf/allocateBlock - local.tee $2 - i32.const 4 - i32.add - local.get $1 - i32.const 4 - i32.add - local.get $1 - i32.load - i32.const -4 - i32.and - call $~lib/memory/memory.copy - local.get $1 - i32.const 2132 - i32.ge_u - if - local.get $0 - local.get $1 - call $~lib/rt/tlsf/freeBlock - end - local.get $2 - ) - (func $~lib/rt/pure/__renew (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - local.get $1 - i32.const 1073741804 - i32.gt_u - if - i32.const 1056 - i32.const 1120 - i32.const 288 - i32.const 30 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.const 16 - i32.sub - local.set $0 - global.get $~lib/rt/tlsf/ROOT - i32.eqz - if - call $~lib/rt/tlsf/initialize - end - local.get $1 - i32.const 16 - i32.add - local.set $2 - local.get $0 - i32.const 2132 - i32.lt_u - if - global.get $~lib/rt/tlsf/ROOT - local.get $0 - call $~lib/rt/tlsf/checkUsedBlock - local.get $2 - call $~lib/rt/tlsf/moveBlock - local.set $0 - else - block $__inlined_func$~lib/rt/tlsf/reallocateBlock - global.get $~lib/rt/tlsf/ROOT - local.set $3 - local.get $0 - call $~lib/rt/tlsf/checkUsedBlock - local.set $0 - block $folding-inner0 - local.get $2 - call $~lib/rt/tlsf/prepareSize - local.tee $5 - local.get $0 - i32.load - local.tee $6 - i32.const -4 - i32.and - local.tee $4 - i32.le_u - br_if $folding-inner0 - local.get $0 - i32.const 4 - i32.add - local.get $0 - i32.load - i32.const -4 - i32.and - i32.add - local.tee $7 - i32.load - local.tee $8 - i32.const 1 - i32.and - if - local.get $5 - local.get $4 - i32.const 4 - i32.add - local.get $8 - i32.const -4 - i32.and - i32.add - local.tee $4 - i32.le_u - if - local.get $3 - local.get $7 - call $~lib/rt/tlsf/removeBlock - local.get $0 - local.get $4 - local.get $6 - i32.const 3 - i32.and - i32.or - i32.store - br $folding-inner0 - end - end - local.get $3 - local.get $0 - local.get $2 - call $~lib/rt/tlsf/moveBlock - local.set $0 - br $__inlined_func$~lib/rt/tlsf/reallocateBlock - end - local.get $3 - local.get $0 - local.get $5 - call $~lib/rt/tlsf/prepareBlock - end - end - local.get $0 - i32.const 4 - i32.add - local.tee $0 - i32.const 4 - i32.sub - local.get $1 - i32.store offset=16 - local.get $0 - i32.const 16 - i32.add - ) - (func $~lib/rt/pure/__retain (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - local.get $0 - i32.const 2132 - i32.gt_u - if - local.get $0 - i32.const 20 - i32.sub - local.tee $1 - i32.load offset=4 - local.tee $2 - i32.const -268435456 - i32.and - local.get $2 - i32.const 1 - i32.add - i32.const -268435456 - i32.and - i32.ne - if - i32.const 0 - i32.const 1120 - i32.const 109 - i32.const 3 - call $~lib/builtins/abort - unreachable - end - local.get $1 - local.get $2 - i32.const 1 - i32.add - i32.store offset=4 - local.get $1 - i32.load - i32.const 1 - i32.and - if - i32.const 0 - i32.const 1120 - i32.const 112 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - end - local.get $0 - ) - (func $~lib/rt/pure/__release (param $0 i32) - local.get $0 - i32.const 2132 - i32.gt_u - if - local.get $0 - i32.const 20 - i32.sub - call $~lib/rt/pure/decrement - end - ) - (func $~lib/memory/memory.fill (param $0 i32) (param $1 i32) - (local $2 i32) - block $~lib/util/memory/memset|inlined.0 - local.get $1 - i32.eqz - br_if $~lib/util/memory/memset|inlined.0 - local.get $0 - i32.const 0 - i32.store8 - local.get $0 - local.get $1 - i32.add - i32.const 4 - i32.sub - local.tee $2 - i32.const 0 - i32.store8 offset=3 - local.get $1 - i32.const 2 - i32.le_u - br_if $~lib/util/memory/memset|inlined.0 - local.get $0 - i32.const 0 - i32.store8 offset=1 - local.get $0 - i32.const 0 - i32.store8 offset=2 - local.get $2 - i32.const 0 - i32.store8 offset=2 - local.get $2 - i32.const 0 - i32.store8 offset=1 - local.get $1 - i32.const 6 - i32.le_u - br_if $~lib/util/memory/memset|inlined.0 - local.get $0 - i32.const 0 - i32.store8 offset=3 - local.get $2 - i32.const 0 - i32.store8 - local.get $1 - i32.const 8 - i32.le_u - br_if $~lib/util/memory/memset|inlined.0 - local.get $0 - i32.const 0 - local.get $0 - i32.sub - i32.const 3 - i32.and - local.tee $2 - i32.add - local.tee $0 - i32.const 0 - i32.store - local.get $0 - local.get $1 - local.get $2 - i32.sub - i32.const -4 - i32.and - local.tee $2 - i32.add - i32.const 28 - i32.sub - local.tee $1 - i32.const 0 - i32.store offset=24 - local.get $2 - i32.const 8 - i32.le_u - br_if $~lib/util/memory/memset|inlined.0 - local.get $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store offset=8 - local.get $1 - i32.const 0 - i32.store offset=16 - local.get $1 - i32.const 0 - i32.store offset=20 - local.get $2 - i32.const 24 - i32.le_u - br_if $~lib/util/memory/memset|inlined.0 - local.get $0 - i32.const 0 - i32.store offset=12 - local.get $0 - i32.const 0 - i32.store offset=16 - local.get $0 - i32.const 0 - i32.store offset=20 - local.get $0 - i32.const 0 - i32.store offset=24 - local.get $1 - i32.const 0 - i32.store - local.get $1 - i32.const 0 - i32.store offset=4 - local.get $1 - i32.const 0 - i32.store offset=8 - local.get $1 - i32.const 0 - i32.store offset=12 - local.get $0 - local.get $0 - i32.const 4 - i32.and - i32.const 24 - i32.add - local.tee $1 - i32.add - local.set $0 - local.get $2 - local.get $1 - i32.sub - local.set $1 - loop $while-continue|0 - local.get $1 - i32.const 32 - i32.ge_u - if - local.get $0 - i64.const 0 - i64.store - local.get $0 - i64.const 0 - i64.store offset=8 - local.get $0 - i64.const 0 - i64.store offset=16 - local.get $0 - i64.const 0 - i64.store offset=24 - local.get $1 - i32.const 32 - i32.sub - local.set $1 - local.get $0 - i32.const 32 - i32.add - local.set $0 - br $while-continue|0 - end - end - end - ) - (func $~lib/arraybuffer/ArrayBufferView#constructor (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - local.get $0 - i32.eqz - if - i32.const 12 - i32.const 2 - call $~lib/rt/pure/__new - call $~lib/rt/pure/__retain - local.set $0 - end - local.get $0 - i32.const 0 - i32.store - local.get $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store offset=8 - local.get $1 - i32.const 1073741820 - local.get $2 - i32.shr_u - i32.gt_u - if - i32.const 1840 - i32.const 1888 - i32.const 18 - i32.const 57 - call $~lib/builtins/abort - unreachable - end - local.get $1 - local.get $2 - i32.shl - local.tee $3 - i32.const 0 - call $~lib/rt/pure/__new - local.tee $1 - local.get $3 - call $~lib/memory/memory.fill - local.get $1 - local.set $2 - local.get $1 - local.get $0 - i32.load - local.tee $4 - i32.ne - if - local.get $2 - call $~lib/rt/pure/__retain - local.set $2 - local.get $4 - call $~lib/rt/pure/__release - end - local.get $0 - local.get $2 - i32.store - local.get $0 - local.get $1 - i32.store offset=4 - local.get $0 - local.get $3 - i32.store offset=8 - local.get $0 - ) - (func $~lib/typedarray/Float64Array#constructor (param $0 i32) (result i32) - i32.const 12 - i32.const 4 - call $~lib/rt/pure/__new - call $~lib/rt/pure/__retain - local.get $0 - i32.const 3 - call $~lib/arraybuffer/ArrayBufferView#constructor - ) - (func $~lib/typedarray/Float64Array#set<~lib/array/Array> (param $0 i32) (param $1 i32) - (local $2 i32) - local.get $1 - call $~lib/rt/pure/__retain - local.set $2 - local.get $0 - call $~lib/rt/pure/__retain - local.set $0 - local.get $2 - call $~lib/rt/pure/__retain - local.tee $1 - i32.load offset=12 - local.get $0 - i32.load offset=8 - i32.const 3 - i32.shr_u - i32.gt_s - if - i32.const 1952 - i32.const 2016 - i32.const 1775 - i32.const 47 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.load offset=4 - local.get $1 - i32.load offset=4 - local.get $1 - i32.load offset=8 - call $~lib/memory/memory.copy - local.get $1 - call $~lib/rt/pure/__release - local.get $0 - call $~lib/rt/pure/__release - local.get $2 - call $~lib/rt/pure/__release - ) - (func $~lib/typedarray/Float64Array#fill (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - local.get $0 - call $~lib/rt/pure/__retain - local.tee $1 - i32.load offset=4 - local.set $3 - i32.const 0 - local.get $1 - i32.load offset=8 - i32.const 3 - i32.shr_u - local.tee $2 - local.get $2 - select - local.set $0 - loop $for-loop|0 - local.get $0 - local.get $2 - i32.lt_s - if - local.get $3 - local.get $0 - i32.const 3 - i32.shl - i32.add - f64.const 0 - f64.store - local.get $0 - i32.const 1 - i32.add - local.set $0 - br $for-loop|0 - end - end - local.get $1 - ) - (func $assembly/index/allocF64Array (param $0 i32) (result i32) - local.get $0 - call $~lib/typedarray/Float64Array#constructor - ) - (func $assembly/index/allocU32Array (param $0 i32) (result i32) - i32.const 12 - i32.const 6 - call $~lib/rt/pure/__new - call $~lib/rt/pure/__retain - local.get $0 - i32.const 2 - call $~lib/arraybuffer/ArrayBufferView#constructor - ) - (func $~lib/typedarray/Float64Array#__get (param $0 i32) (param $1 i32) (result f64) - local.get $1 - local.get $0 - i32.load offset=8 - i32.const 3 - i32.shr_u - i32.ge_u - if - i32.const 1952 - i32.const 2016 - i32.const 1304 - i32.const 64 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.load offset=4 - local.get $1 - i32.const 3 - i32.shl - i32.add - f64.load - ) - (func $~lib/typedarray/Float64Array#__set (param $0 i32) (param $1 i32) (param $2 f64) - local.get $1 - local.get $0 - i32.load offset=8 - i32.const 3 - i32.shr_u - i32.ge_u - if - i32.const 1952 - i32.const 2016 - i32.const 1315 - i32.const 64 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.load offset=4 - local.get $1 - i32.const 3 - i32.shl - i32.add - local.get $2 - f64.store - ) - (func $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> (param $0 i32) (param $1 i32) - (local $2 i32) - local.get $1 - call $~lib/rt/pure/__retain - local.set $2 - local.get $0 - call $~lib/rt/pure/__retain - local.set $0 - local.get $2 - call $~lib/rt/pure/__retain - local.tee $1 - i32.load offset=8 - i32.const 3 - i32.shr_u - local.get $0 - i32.load offset=8 - i32.const 3 - i32.shr_u - i32.gt_s - if - i32.const 1952 - i32.const 2016 - i32.const 1775 - i32.const 47 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.load offset=4 - local.get $1 - i32.load offset=4 - local.get $1 - i32.load offset=8 - call $~lib/memory/memory.copy - local.get $1 - call $~lib/rt/pure/__release - local.get $0 - call $~lib/rt/pure/__release - local.get $2 - call $~lib/rt/pure/__release - ) - (func $~lib/math/NativeMath.pow (param $0 f64) (param $1 f64) (result f64) - (local $2 i32) - (local $3 i32) - (local $4 f64) - (local $5 i32) - (local $6 i32) - (local $7 f64) - (local $8 f64) - (local $9 f64) - (local $10 f64) - (local $11 i64) - (local $12 i32) - (local $13 i32) - (local $14 i32) - (local $15 i32) - (local $16 f64) - (local $17 i32) - (local $18 f64) - (local $19 f64) - block $folding-inner3 - block $folding-inner2 - block $folding-inner1 - block $folding-inner0 - local.get $1 - f64.abs - f64.const 2 - f64.le - if - local.get $1 - f64.const 2 - f64.eq - br_if $folding-inner0 - local.get $1 - f64.const 0.5 - f64.eq - if - local.get $0 - f64.sqrt - f64.abs - f64.const inf - local.get $0 - f64.const -inf - f64.ne - select - return - end - local.get $1 - f64.const -1 - f64.eq - br_if $folding-inner1 - local.get $1 - f64.const 1 - f64.eq - if - local.get $0 - return - end - local.get $1 - f64.const 0 - f64.eq - if - f64.const 1 - return - end - end - local.get $0 - i64.reinterpret_f64 - local.tee $11 - i32.wrap_i64 - local.set $13 - local.get $11 - i64.const 32 - i64.shr_u - i32.wrap_i64 - local.tee $12 - i32.const 2147483647 - i32.and - local.set $5 - local.get $1 - i64.reinterpret_f64 - local.tee $11 - i64.const 32 - i64.shr_u - i32.wrap_i64 - local.tee $3 - i32.const 2147483647 - i32.and - local.tee $6 - local.get $11 - i32.wrap_i64 - local.tee $14 - i32.or - i32.eqz - if - f64.const 1 - return - end - i32.const 1 - local.get $14 - i32.const 0 - local.get $6 - i32.const 2146435072 - i32.eq - select - i32.const 1 - local.get $6 - i32.const 2146435072 - i32.gt_u - i32.const 1 - local.get $13 - i32.const 0 - local.get $5 - i32.const 2146435072 - i32.eq - select - local.get $5 - i32.const 2146435072 - i32.gt_s - select - select - select - if - local.get $0 - local.get $1 - f64.add - return - end - local.get $12 - i32.const 0 - i32.lt_s - if (result i32) - local.get $6 - i32.const 1128267776 - i32.ge_u - if (result i32) - i32.const 2 - else - local.get $6 - i32.const 1072693248 - i32.ge_u - if (result i32) - i32.const 2 - local.get $14 - local.get $6 - local.get $6 - i32.const 20 - i32.shr_u - i32.const 1023 - i32.sub - local.tee $2 - i32.const 20 - i32.gt_s - local.tee $15 - select - local.tee $17 - i32.const 52 - i32.const 20 - local.get $15 - select - local.get $2 - i32.sub - local.tee $2 - i32.shr_u - local.tee $15 - i32.const 1 - i32.and - i32.sub - i32.const 0 - local.get $17 - local.get $15 - local.get $2 - i32.shl - i32.eq - select - else - i32.const 0 - end - end - else - i32.const 0 - end - local.set $2 - local.get $14 - i32.eqz - if - local.get $6 - i32.const 2146435072 - i32.eq - if - local.get $13 - local.get $5 - i32.const 1072693248 - i32.sub - i32.or - if - local.get $5 - i32.const 1072693248 - i32.ge_s - if - local.get $1 - f64.const 0 - local.get $3 - i32.const 0 - i32.ge_s - select - return - else - f64.const 0 - local.get $1 - f64.neg - local.get $3 - i32.const 0 - i32.ge_s - select - return - end - unreachable - else - f64.const nan:0x8000000000000 - return - end - unreachable - end - local.get $6 - i32.const 1072693248 - i32.eq - if - local.get $3 - i32.const 0 - i32.ge_s - if - local.get $0 - return - end - br $folding-inner1 - end - local.get $3 - i32.const 1073741824 - i32.eq - br_if $folding-inner0 - local.get $3 - i32.const 1071644672 - i32.eq - if - local.get $12 - i32.const 0 - i32.ge_s - if - local.get $0 - f64.sqrt - return - end - end - end - local.get $0 - f64.abs - local.set $4 - local.get $13 - i32.eqz - if - i32.const 1 - local.get $5 - i32.const 1072693248 - i32.eq - local.get $5 - i32.const 2146435072 - i32.eq - i32.const 1 - local.get $5 - select - select - if - f64.const 1 - local.get $4 - f64.div - local.get $4 - local.get $3 - i32.const 0 - i32.lt_s - select - local.set $0 - local.get $12 - i32.const 0 - i32.lt_s - if (result f64) - local.get $2 - local.get $5 - i32.const 1072693248 - i32.sub - i32.or - if (result f64) - local.get $0 - f64.neg - local.get $0 - local.get $2 - i32.const 1 - i32.eq - select - else - local.get $0 - local.get $0 - f64.sub - local.tee $0 - local.get $0 - f64.div - end - else - local.get $0 - end - return - end - end - local.get $12 - i32.const 0 - i32.lt_s - if (result f64) - local.get $2 - i32.eqz - if - local.get $0 - local.get $0 - f64.sub - local.tee $0 - local.get $0 - f64.div - return - end - f64.const -1 - f64.const 1 - local.get $2 - i32.const 1 - i32.eq - select - else - f64.const 1 - end - local.set $8 - local.get $6 - i32.const 1105199104 - i32.gt_u - if (result f64) - local.get $6 - i32.const 1139802112 - i32.gt_u - if - local.get $5 - i32.const 1072693247 - i32.le_s - if - f64.const inf - f64.const 0 - local.get $3 - i32.const 0 - i32.lt_s - select - return - end - local.get $5 - i32.const 1072693248 - i32.ge_s - if - f64.const inf - f64.const 0 - local.get $3 - i32.const 0 - i32.gt_s - select - return - end - end - local.get $5 - i32.const 1072693247 - i32.lt_s - if - local.get $8 - f64.const 1.e+300 - f64.mul - f64.const 1.e+300 - f64.mul - local.get $8 - f64.const 1e-300 - f64.mul - f64.const 1e-300 - f64.mul - local.get $3 - i32.const 0 - i32.lt_s - select - return - end - local.get $5 - i32.const 1072693248 - i32.gt_s - if - local.get $8 - f64.const 1.e+300 - f64.mul - f64.const 1.e+300 - f64.mul - local.get $8 - f64.const 1e-300 - f64.mul - f64.const 1e-300 - f64.mul - local.get $3 - i32.const 0 - i32.gt_s - select - return - end - local.get $4 - f64.const 1 - f64.sub - local.tee $0 - f64.const 1.4426950216293335 - f64.mul - local.tee $4 - local.get $0 - f64.const 1.9259629911266175e-08 - f64.mul - local.get $0 - local.get $0 - f64.mul - f64.const 0.5 - local.get $0 - f64.const 0.3333333333333333 - local.get $0 - f64.const 0.25 - f64.mul - f64.sub - f64.mul - f64.sub - f64.mul - f64.const 1.4426950408889634 - f64.mul - f64.sub - local.tee $7 - f64.add - i64.reinterpret_f64 - i64.const -4294967296 - i64.and - f64.reinterpret_i64 - local.set $0 - local.get $7 - local.get $0 - local.get $4 - f64.sub - f64.sub - else - local.get $5 - i32.const 1048576 - i32.lt_s - if (result i32) - local.get $4 - f64.const 9007199254740992 - f64.mul - local.tee $4 - i64.reinterpret_f64 - i64.const 32 - i64.shr_u - i32.wrap_i64 - local.set $5 - i32.const -53 - else - i32.const 0 - end - local.get $5 - i32.const 20 - i32.shr_s - i32.const 1023 - i32.sub - i32.add - local.set $3 - local.get $5 - i32.const 1048575 - i32.and - local.tee $2 - i32.const 1072693248 - i32.or - local.set $5 - local.get $2 - i32.const 235662 - i32.le_s - if (result i32) - i32.const 0 - else - local.get $2 - i32.const 767610 - i32.lt_s - if (result i32) - i32.const 1 - else - local.get $3 - i32.const 1 - i32.add - local.set $3 - local.get $5 - i32.const -1048576 - i32.add - local.set $5 - i32.const 0 - end - end - local.set $2 - local.get $4 - i64.reinterpret_f64 - i64.const 4294967295 - i64.and - local.get $5 - i64.extend_i32_s - i64.const 32 - i64.shl - i64.or - f64.reinterpret_i64 - local.tee $9 - f64.const 1.5 - f64.const 1 - local.get $2 - select - local.tee $10 - f64.sub - local.tee $18 - f64.const 1 - local.get $9 - local.get $10 - f64.add - f64.div - local.tee $19 - f64.mul - local.tee $7 - i64.reinterpret_f64 - i64.const -4294967296 - i64.and - f64.reinterpret_i64 - local.tee $4 - local.get $4 - f64.mul - local.set $16 - local.get $4 - local.get $16 - f64.const 3 - f64.add - local.get $7 - local.get $7 - f64.mul - local.tee $0 - local.get $0 - f64.mul - local.get $0 - local.get $0 - local.get $0 - local.get $0 - local.get $0 - f64.const 0.20697501780033842 - f64.mul - f64.const 0.23066074577556175 - f64.add - f64.mul - f64.const 0.272728123808534 - f64.add - f64.mul - f64.const 0.33333332981837743 - f64.add - f64.mul - f64.const 0.4285714285785502 - f64.add - f64.mul - f64.const 0.5999999999999946 - f64.add - f64.mul - local.get $19 - local.get $18 - local.get $4 - local.get $5 - i32.const 1 - i32.shr_s - i32.const 536870912 - i32.or - i32.const 524288 - i32.add - local.get $2 - i32.const 18 - i32.shl - i32.add - i64.extend_i32_s - i64.const 32 - i64.shl - f64.reinterpret_i64 - local.tee $0 - f64.mul - f64.sub - local.get $4 - local.get $9 - local.get $0 - local.get $10 - f64.sub - f64.sub - f64.mul - f64.sub - f64.mul - local.tee $9 - local.get $4 - local.get $7 - f64.add - f64.mul - f64.add - local.tee $4 - f64.add - i64.reinterpret_f64 - i64.const -4294967296 - i64.and - f64.reinterpret_i64 - local.tee $0 - f64.mul - local.tee $10 - local.get $9 - local.get $0 - f64.mul - local.get $4 - local.get $0 - f64.const 3 - f64.sub - local.get $16 - f64.sub - f64.sub - local.get $7 - f64.mul - f64.add - local.tee $4 - f64.add - i64.reinterpret_f64 - i64.const -4294967296 - i64.and - f64.reinterpret_i64 - local.tee $0 - f64.const 0.9617967009544373 - f64.mul - local.tee $7 - local.get $0 - f64.const -7.028461650952758e-09 - f64.mul - local.get $4 - local.get $0 - local.get $10 - f64.sub - f64.sub - f64.const 0.9617966939259756 - f64.mul - f64.add - f64.const 1.350039202129749e-08 - f64.const 0 - local.get $2 - select - f64.add - local.tee $4 - f64.add - f64.const 0.5849624872207642 - f64.const 0 - local.get $2 - select - local.tee $9 - f64.add - local.get $3 - f64.convert_i32_s - local.tee $10 - f64.add - i64.reinterpret_f64 - i64.const -4294967296 - i64.and - f64.reinterpret_i64 - local.set $0 - local.get $4 - local.get $0 - local.get $10 - f64.sub - local.get $9 - f64.sub - local.get $7 - f64.sub - f64.sub - end - local.set $4 - local.get $1 - local.get $1 - i64.reinterpret_f64 - i64.const -4294967296 - i64.and - f64.reinterpret_i64 - local.tee $7 - f64.sub - local.get $0 - f64.mul - local.get $1 - local.get $4 - f64.mul - f64.add - local.tee $1 - local.get $7 - local.get $0 - f64.mul - local.tee $0 - f64.add - local.tee $4 - i64.reinterpret_f64 - local.tee $11 - i32.wrap_i64 - local.set $3 - local.get $11 - i64.const 32 - i64.shr_u - i32.wrap_i64 - local.tee $2 - i32.const 1083179008 - i32.ge_s - if - local.get $3 - local.get $2 - i32.const 1083179008 - i32.sub - i32.or - local.get $1 - f64.const 8.008566259537294e-17 - f64.add - local.get $4 - local.get $0 - f64.sub - f64.gt - i32.or - br_if $folding-inner2 - else - local.get $2 - i32.const 2147483647 - i32.and - i32.const 1083231232 - i32.ge_u - i32.const 0 - local.get $3 - local.get $2 - i32.const -1064252416 - i32.sub - i32.or - local.get $1 - local.get $4 - local.get $0 - f64.sub - f64.le - i32.or - select - br_if $folding-inner3 - end - local.get $2 - i32.const 2147483647 - i32.and - local.tee $5 - i32.const 20 - i32.shr_u - i32.const 1023 - i32.sub - local.set $6 - i32.const 0 - local.set $3 - local.get $1 - local.get $5 - i32.const 1071644672 - i32.gt_s - if - local.get $2 - i32.const 1048576 - local.get $6 - i32.const 1 - i32.add - i32.shr_s - i32.add - local.tee $5 - i32.const 2147483647 - i32.and - i32.const 20 - i32.shr_u - i32.const 1023 - i32.sub - local.set $6 - i32.const 0 - local.get $5 - i32.const 1048575 - i32.and - i32.const 1048576 - i32.or - i32.const 20 - local.get $6 - i32.sub - i32.shr_s - local.tee $3 - i32.sub - local.get $3 - local.get $2 - i32.const 0 - i32.lt_s - select - local.set $3 - local.get $0 - local.get $5 - i32.const 1048575 - local.get $6 - i32.shr_s - i32.const -1 - i32.xor - i32.and - i64.extend_i32_s - i64.const 32 - i64.shl - f64.reinterpret_i64 - f64.sub - local.set $0 - end - local.get $0 - f64.add - i64.reinterpret_f64 - i64.const -4294967296 - i64.and - f64.reinterpret_i64 - local.tee $4 - f64.const 0.6931471824645996 - f64.mul - local.tee $7 - local.get $1 - local.get $4 - local.get $0 - f64.sub - f64.sub - f64.const 0.6931471805599453 - f64.mul - local.get $4 - f64.const -1.904654299957768e-09 - f64.mul - f64.add - local.tee $4 - f64.add - local.tee $0 - local.get $0 - f64.mul - local.set $1 - local.get $8 - f64.const 1 - local.get $0 - local.get $0 - local.get $1 - local.get $1 - local.get $1 - local.get $1 - local.get $1 - f64.const 4.1381367970572385e-08 - f64.mul - f64.const -1.6533902205465252e-06 - f64.add - f64.mul - f64.const 6.613756321437934e-05 - f64.add - f64.mul - f64.const -2.7777777777015593e-03 - f64.add - f64.mul - f64.const 0.16666666666666602 - f64.add - f64.mul - f64.sub - local.tee $1 - f64.mul - local.get $1 - f64.const 2 - f64.sub - f64.div - local.get $4 - local.get $0 - local.get $7 - f64.sub - f64.sub - local.tee $1 - local.get $0 - local.get $1 - f64.mul - f64.add - f64.sub - local.get $0 - f64.sub - f64.sub - local.tee $0 - i64.reinterpret_f64 - i64.const 32 - i64.shr_u - i32.wrap_i64 - local.get $3 - i32.const 20 - i32.shl - i32.add - local.tee $2 - i32.const 20 - i32.shr_s - i32.const 0 - i32.le_s - if (result f64) - local.get $3 - local.tee $2 - i32.const 1023 - i32.gt_s - if (result f64) - local.get $0 - f64.const 8988465674311579538646525e283 - f64.mul - local.set $0 - local.get $2 - i32.const 1023 - i32.sub - local.tee $2 - i32.const 1023 - i32.gt_s - if (result f64) - local.get $2 - i32.const 1023 - i32.sub - local.tee $2 - i32.const 1023 - local.get $2 - i32.const 1023 - i32.lt_s - select - local.set $2 - local.get $0 - f64.const 8988465674311579538646525e283 - f64.mul - else - local.get $0 - end - else - local.get $2 - i32.const -1022 - i32.lt_s - if (result f64) - local.get $0 - f64.const 2.004168360008973e-292 - f64.mul - local.set $0 - local.get $2 - i32.const 969 - i32.add - local.tee $2 - i32.const -1022 - i32.lt_s - if (result f64) - local.get $2 - i32.const 969 - i32.add - local.tee $2 - i32.const -1022 - local.get $2 - i32.const -1022 - i32.gt_s - select - local.set $2 - local.get $0 - f64.const 2.004168360008973e-292 - f64.mul - else - local.get $0 - end - else - local.get $0 - end - end - local.get $2 - i64.extend_i32_s - i64.const 1023 - i64.add - i64.const 52 - i64.shl - f64.reinterpret_i64 - f64.mul - else - local.get $0 - i64.reinterpret_f64 - i64.const 4294967295 - i64.and - local.get $2 - i64.extend_i32_s - i64.const 32 - i64.shl - i64.or - f64.reinterpret_i64 - end - f64.mul - return - end - local.get $0 - local.get $0 - f64.mul - return - end - f64.const 1 - local.get $0 - f64.div - return - end - local.get $8 - f64.const 1.e+300 - f64.mul - f64.const 1.e+300 - f64.mul - return - end - local.get $8 - f64.const 1e-300 - f64.mul - f64.const 1e-300 - f64.mul - ) - (func $assembly/index/update - (local $0 f64) - (local $1 f64) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 f64) - (local $10 f64) - (local $11 f64) - (local $12 f64) - (local $13 f64) - (local $14 f64) - (local $15 i32) - global.get $assembly/index/audioSettings - i32.const 0 - call $~lib/typedarray/Float64Array#__get - f64.const 0 - f64.gt - if - global.get $assembly/index/inputData - call $~lib/rt/pure/__retain - local.set $4 - i32.const 128 - call $~lib/typedarray/Float64Array#constructor - local.set $5 - loop $for-loop|0 - local.get $3 - i32.const 64 - i32.lt_s - if - local.get $5 - local.get $3 - local.get $4 - local.get $3 - call $~lib/typedarray/Float64Array#__get - global.get $assembly/index/pinkNoise - local.get $3 - call $~lib/typedarray/Float64Array#__get - f64.div - call $~lib/typedarray/Float64Array#__set - local.get $5 - local.get $3 - i32.const -64 - i32.sub - local.tee $7 - local.get $4 - local.get $7 - call $~lib/typedarray/Float64Array#__get - global.get $assembly/index/pinkNoise - local.get $3 - call $~lib/typedarray/Float64Array#__get - f64.div - call $~lib/typedarray/Float64Array#__set - local.get $3 - i32.const 1 - i32.add - local.set $3 - br $for-loop|0 - end - end - local.get $4 - local.get $5 - call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> - local.get $4 - call $~lib/rt/pure/__release - local.get $5 - call $~lib/rt/pure/__release - end - global.get $assembly/index/audioSettings - i32.const 1 - call $~lib/typedarray/Float64Array#__get - f64.const 0 - f64.gt - if - i32.const 0 - local.set $3 - i32.const 0 - local.set $7 - global.get $assembly/index/inputData - call $~lib/rt/pure/__retain - local.set $4 - i32.const 128 - call $~lib/typedarray/Float64Array#constructor - local.set $5 - loop $for-loop|01 - local.get $3 - i32.const 64 - i32.lt_s - if - local.get $5 - local.get $7 - local.get $4 - local.get $3 - call $~lib/typedarray/Float64Array#__get - call $~lib/typedarray/Float64Array#__set - local.get $7 - i32.const 1 - i32.add - local.tee $15 - i32.const 1 - i32.add - local.set $7 - local.get $5 - local.get $15 - local.get $4 - local.get $3 - i32.const -64 - i32.sub - call $~lib/typedarray/Float64Array#__get - call $~lib/typedarray/Float64Array#__set - local.get $3 - i32.const 1 - i32.add - local.set $3 - br $for-loop|01 - end - end - local.get $4 - local.get $5 - call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> - local.get $4 - call $~lib/rt/pure/__release - local.get $5 - call $~lib/rt/pure/__release - end - global.get $assembly/index/audioSettings - i32.const 2 - call $~lib/typedarray/Float64Array#__get - f64.const 0 - f64.gt - if - global.get $assembly/index/audioSettings - i32.const 1 - call $~lib/typedarray/Float64Array#__get - f64.const 0 - f64.gt - if - global.get $assembly/index/inputData - call $~lib/rt/pure/__retain - local.set $3 - loop $for-loop|04 - local.get $2 - i32.const 64 - i32.lt_s - if - local.get $3 - local.get $2 - call $~lib/typedarray/Float64Array#__get - local.set $1 - local.get $3 - local.get $2 - local.get $3 - i32.const 127 - local.get $2 - i32.sub - local.tee $4 - call $~lib/typedarray/Float64Array#__get - call $~lib/typedarray/Float64Array#__set - local.get $3 - local.get $4 - local.get $1 - call $~lib/typedarray/Float64Array#__set - local.get $2 - i32.const 1 - i32.add - local.set $2 - br $for-loop|04 - end - end - local.get $3 - call $~lib/rt/pure/__release - else - global.get $assembly/index/inputData - call $~lib/rt/pure/__retain - local.set $3 - loop $for-loop|00 - local.get $2 - i32.const 32 - i32.lt_s - if - local.get $3 - local.get $2 - call $~lib/typedarray/Float64Array#__get - local.set $1 - local.get $3 - local.get $2 - local.get $3 - i32.const 63 - local.get $2 - i32.sub - local.tee $4 - call $~lib/typedarray/Float64Array#__get - call $~lib/typedarray/Float64Array#__set - local.get $3 - local.get $4 - local.get $1 - call $~lib/typedarray/Float64Array#__set - local.get $2 - i32.const 1 - i32.add - local.set $2 - br $for-loop|00 - end - end - local.get $3 - call $~lib/rt/pure/__release - end - else - global.get $assembly/index/audioSettings - i32.const 1 - call $~lib/typedarray/Float64Array#__get - f64.const 0 - f64.gt - if - global.get $assembly/index/inputData - call $~lib/rt/pure/__retain - local.set $2 - loop $for-loop|016 - local.get $6 - i32.const 32 - i32.lt_s - if - local.get $2 - local.get $6 - i32.const -64 - i32.sub - local.tee $3 - call $~lib/typedarray/Float64Array#__get - local.set $1 - local.get $2 - local.get $3 - local.get $2 - i32.const 127 - local.get $6 - i32.sub - local.tee $3 - call $~lib/typedarray/Float64Array#__get - call $~lib/typedarray/Float64Array#__set - local.get $2 - local.get $3 - local.get $1 - call $~lib/typedarray/Float64Array#__set - local.get $6 - i32.const 1 - i32.add - local.set $6 - br $for-loop|016 - end - end - local.get $2 - call $~lib/rt/pure/__release - end - end - global.get $assembly/index/audioSettings - i32.const 3 - call $~lib/typedarray/Float64Array#__get - f64.const 0 - f64.gt - if - global.get $assembly/index/inputData - global.get $assembly/index/audioSettings - i32.const 3 - call $~lib/typedarray/Float64Array#__get - f64.const 1 - f64.add - local.set $11 - i32.const 0 - local.set $2 - f64.const 0 - local.set $1 - call $~lib/rt/pure/__retain - local.set $3 - i32.const 128 - call $~lib/typedarray/Float64Array#constructor - local.set $4 - loop $for-loop|08 - local.get $2 - i32.const 128 - i32.lt_s - if - local.get $3 - local.get $2 - call $~lib/typedarray/Float64Array#__get - local.get $1 - f64.gt - if - local.get $3 - local.get $2 - call $~lib/typedarray/Float64Array#__get - local.set $1 - end - local.get $4 - local.get $2 - local.get $3 - local.get $2 - call $~lib/typedarray/Float64Array#__get - local.get $11 - f64.mul - local.get $11 - call $~lib/math/NativeMath.pow - call $~lib/typedarray/Float64Array#__set - local.get $4 - local.get $2 - call $~lib/typedarray/Float64Array#__get - local.get $0 - f64.gt - if - local.get $4 - local.get $2 - call $~lib/typedarray/Float64Array#__get - local.set $0 - end - local.get $2 - i32.const 1 - i32.add - local.set $2 - br $for-loop|08 - end - end - local.get $0 - local.get $1 - f64.div - local.set $1 - i32.const 0 - local.set $2 - loop $for-loop|1 - local.get $2 - i32.const 128 - i32.lt_s - if - local.get $3 - local.get $2 - local.get $4 - local.get $2 - call $~lib/typedarray/Float64Array#__get - local.get $1 - f64.div - call $~lib/typedarray/Float64Array#__set - local.get $2 - i32.const 1 - i32.add - local.set $2 - br $for-loop|1 - end - end - local.get $3 - call $~lib/rt/pure/__release - local.get $4 - call $~lib/rt/pure/__release - end - global.get $assembly/index/audioSettings - i32.const 4 - call $~lib/typedarray/Float64Array#__get - f64.const 0 - f64.gt - if - global.get $assembly/index/inputData - global.get $assembly/index/audioSettings - i32.const 4 - call $~lib/typedarray/Float64Array#__get - f64.floor - i32.trunc_f64_s - local.set $4 - i32.const 0 - local.set $2 - call $~lib/rt/pure/__retain - local.set $5 - i32.const 128 - call $~lib/typedarray/Float64Array#constructor - local.set $6 - loop $for-loop|010 - local.get $2 - i32.const 128 - i32.lt_s - if - f64.const 0 - local.set $1 - local.get $2 - local.get $4 - i32.sub - local.set $3 - local.get $2 - local.get $4 - i32.add - local.set $7 - loop $for-loop|111 - local.get $3 - local.get $7 - i32.le_s - if - local.get $1 - local.get $5 - local.get $3 - i32.const 128 - i32.add - local.get $3 - local.get $3 - i32.const 0 - i32.lt_s - select - i32.const 128 - i32.rem_s - call $~lib/typedarray/Float64Array#__get - f64.add - local.set $1 - local.get $3 - i32.const 1 - i32.add - local.set $3 - br $for-loop|111 - end - end - local.get $6 - local.get $2 - local.get $1 - local.get $4 - i32.const 1 - i32.shl - i32.const 1 - i32.add - f64.convert_i32_s - f64.div - call $~lib/typedarray/Float64Array#__set - local.get $2 - i32.const 1 - i32.add - local.set $2 - br $for-loop|010 - end - end - local.get $5 - local.get $6 - call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> - local.get $5 - call $~lib/rt/pure/__release - local.get $6 - call $~lib/rt/pure/__release - end - global.get $assembly/index/inputData - global.get $assembly/index/outputData - local.set $4 - global.get $assembly/index/audioSettings - i32.const 5 - call $~lib/typedarray/Float64Array#__get - local.set $0 - global.get $assembly/index/audioSettings - i32.const 6 - call $~lib/typedarray/Float64Array#__get - local.set $11 - i32.const 0 - local.set $2 - call $~lib/rt/pure/__retain - local.set $3 - local.get $4 - call $~lib/rt/pure/__retain - local.set $4 - loop $for-loop|012 - local.get $2 - i32.const 128 - i32.lt_s - if - local.get $3 - local.get $2 - call $~lib/typedarray/Float64Array#__get - local.get $4 - local.get $2 - call $~lib/typedarray/Float64Array#__get - f64.sub - local.set $1 - local.get $3 - local.get $2 - local.get $3 - local.get $2 - call $~lib/typedarray/Float64Array#__get - local.get $1 - f64.const 100 - local.get $0 - local.get $11 - local.get $1 - f64.const 0 - f64.gt - select - f64.sub - f64.mul - f64.const 100 - f64.div - f64.sub - call $~lib/typedarray/Float64Array#__set - local.get $2 - i32.const 1 - i32.add - local.set $2 - br $for-loop|012 - end - end - local.get $3 - call $~lib/rt/pure/__release - local.get $4 - call $~lib/rt/pure/__release - f64.const 1 - local.set $0 - loop $for-loop|02 - local.get $8 - i32.const 128 - i32.lt_s - if - local.get $0 - global.get $assembly/index/inputData - local.get $8 - call $~lib/typedarray/Float64Array#__get - local.tee $1 - f64.gt - if - local.get $1 - local.set $0 - end - local.get $1 - local.get $9 - local.get $1 - local.get $9 - f64.gt - select - local.set $9 - local.get $8 - i32.const 14 - i32.lt_s - if - local.get $12 - local.get $1 - global.get $assembly/index/audioSettings - i32.const 9 - call $~lib/typedarray/Float64Array#__get - f64.mul - f64.add - local.set $12 - else - local.get $8 - i32.const 69 - i32.gt_s - if - local.get $13 - local.get $1 - global.get $assembly/index/audioSettings - i32.const 7 - call $~lib/typedarray/Float64Array#__get - f64.mul - f64.add - local.set $13 - else - local.get $14 - local.get $1 - global.get $assembly/index/audioSettings - i32.const 8 - call $~lib/typedarray/Float64Array#__get - f64.mul - f64.add - local.set $14 - end - end - local.get $10 - local.get $1 - f64.add - local.set $10 - local.get $8 - i32.const 1 - i32.add - local.set $8 - br $for-loop|02 - end - end - f64.const 0.9999 - f64.const 0 - local.get $9 - global.get $assembly/index/audioSettings - i32.const 10 - call $~lib/typedarray/Float64Array#__get - f64.const 1e3 - f64.div - f64.lt - select - local.set $1 - global.get $assembly/index/outputData - global.get $assembly/index/inputData - call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> - global.get $assembly/index/audioProps - i32.const 16 - i32.const 3 - call $~lib/rt/pure/__new - local.tee $2 - i32.const 80 - i32.const 0 - call $~lib/rt/pure/__new - local.tee $3 - call $~lib/rt/pure/__retain - i32.store - local.get $2 - local.get $3 - i32.store offset=4 - local.get $2 - i32.const 80 - i32.store offset=8 - local.get $2 - i32.const 10 - i32.store offset=12 - local.get $2 - call $~lib/rt/pure/__retain - local.tee $3 - i32.load offset=4 - local.tee $2 - local.get $12 - f64.store - local.get $2 - local.get $14 - f64.store offset=8 - local.get $2 - local.get $13 - f64.store offset=16 - local.get $2 - local.get $10 - f64.store offset=24 - local.get $2 - local.get $0 - f64.store offset=32 - local.get $2 - local.get $9 - f64.store offset=40 - local.get $2 - local.get $10 - f64.const 0.0078125 - f64.mul - local.tee $10 - f64.store offset=48 - local.get $2 - local.get $9 - local.get $0 - f64.sub - f64.store offset=56 - local.get $2 - local.get $1 - f64.store offset=64 - local.get $2 - local.get $12 - f64.const 8 - f64.mul - local.get $14 - f64.sub - local.get $13 - f64.add - f64.const 6 - f64.div - local.get $10 - f64.div - f64.store offset=72 - local.get $3 - call $~lib/typedarray/Float64Array#set<~lib/array/Array> - local.get $3 - call $~lib/rt/pure/__release - ) - (func $~start - i32.const 64 - call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/pinkNoise - global.get $assembly/index/pinkNoise - i32.const 1792 - call $~lib/typedarray/Float64Array#set<~lib/array/Array> - i32.const 128 - call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/inputData - global.get $assembly/index/inputData - call $~lib/typedarray/Float64Array#fill - call $~lib/rt/pure/__release - i32.const 128 - call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/outputData - global.get $assembly/index/outputData - call $~lib/typedarray/Float64Array#fill - call $~lib/rt/pure/__release - i32.const 10 - call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/audioProps - global.get $assembly/index/audioProps - call $~lib/typedarray/Float64Array#fill - call $~lib/rt/pure/__release - i32.const 11 - call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/audioSettings - global.get $assembly/index/audioSettings - call $~lib/typedarray/Float64Array#fill - call $~lib/rt/pure/__release - ) - (func $~lib/rt/pure/decrement (param $0 i32) - (local $1 i32) - (local $2 i32) - local.get $0 - i32.load offset=4 - local.tee $2 - i32.const 268435455 - i32.and - local.set $1 - local.get $0 - i32.load - i32.const 1 - i32.and - if - i32.const 0 - i32.const 1120 - i32.const 122 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.const 1 - i32.eq - if - block $__inlined_func$~lib/rt/__visit_members - block $folding-inner2 - block $folding-inner1 - block $invalid - block $~lib/arraybuffer/ArrayBufferView - local.get $0 - i32.const 12 - i32.add - i32.load - br_table $__inlined_func$~lib/rt/__visit_members $__inlined_func$~lib/rt/__visit_members $~lib/arraybuffer/ArrayBufferView $folding-inner1 $folding-inner2 $folding-inner2 $folding-inner2 $folding-inner1 $invalid - end - local.get $0 - i32.load offset=20 - local.tee $1 - if - local.get $1 - call $~lib/rt/pure/__visit - end - br $__inlined_func$~lib/rt/__visit_members - end - unreachable - end - local.get $0 - i32.load offset=20 - call $~lib/rt/pure/__visit - br $__inlined_func$~lib/rt/__visit_members - end - local.get $0 - i32.load offset=20 - local.tee $1 - if - local.get $1 - call $~lib/rt/pure/__visit - end - end - local.get $2 - i32.const -2147483648 - i32.and - if - i32.const 0 - i32.const 1120 - i32.const 126 - i32.const 18 - call $~lib/builtins/abort - unreachable - end - global.get $~lib/rt/tlsf/ROOT - local.get $0 - call $~lib/rt/tlsf/freeBlock - else - local.get $1 - i32.eqz - if - i32.const 0 - i32.const 1120 - i32.const 136 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $1 - i32.const 1 - i32.sub - local.get $2 - i32.const -268435456 - i32.and - i32.or - i32.store offset=4 - end - ) - (func $~lib/rt/pure/__visit (param $0 i32) - local.get $0 - i32.const 2132 - i32.lt_u - if - return - end - local.get $0 - i32.const 20 - i32.sub - call $~lib/rt/pure/decrement - ) -) diff --git a/src/wasc-builder/build/untouched.wat b/src/wasc-builder/build/untouched.wat deleted file mode 100644 index 5801fb8..0000000 --- a/src/wasc-builder/build/untouched.wat +++ /dev/null @@ -1,5794 +0,0 @@ -(module - (type $i32_i32_=>_none (func (param i32 i32))) - (type $i32_=>_none (func (param i32))) - (type $i32_=>_i32 (func (param i32) (result i32))) - (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) - (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) - (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) - (type $none_=>_none (func)) - (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) - (type $i32_i32_f64_=>_none (func (param i32 i32 f64))) - (type $i32_i32_f64_f64_=>_none (func (param i32 i32 f64 f64))) - (type $i32_f64_=>_none (func (param i32 f64))) - (type $i32_i32_i32_i32_=>_i32 (func (param i32 i32 i32 i32) (result i32))) - (type $i32_f64_i32_i32_=>_i32 (func (param i32 f64 i32 i32) (result i32))) - (type $f64_=>_i32 (func (param f64) (result i32))) - (type $i32_i32_=>_f64 (func (param i32 i32) (result f64))) - (type $f64_f64_=>_f64 (func (param f64 f64) (result f64))) - (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) - (memory $0 1) - (data (i32.const 12) "(\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00(\00\00\00a\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e\00") - (data (i32.const 76) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00p\00u\00r\00e\00.\00t\00s\00") - (data (i32.const 140) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00") - (data (i32.const 204) "\00\02\00\00\01\00\00\00\00\00\00\00\00\00\00\00\00\02\00\00Z>v\e8\0b\d1\f2?yY\f9B0D\eb?\0d\94\96\8c\92\07\e6?Xd\18\d8\ddg\e4?\deuPs\90r\e1?G\e7)>A;\e0?\83\a4\f0\95\fc\ef\dd?\ef\82!\ceiJ\dc?\ea\'\89\e7\'\da\da?V\0c\08\ec4\92\da?\94?\8d\d7\93p\da?\b8^\11\aa\e9\fe\d9?P\f6\aa:v\8b\d9?.y1\17\d6s\d8?\aa\f6\af\e0\f3\87\d8?Tq\16So\fb\d7?\a0!\c7\07Rj\d7?\af\866\dc\e0\10\d8?\c4\1eP\ef\b3r\d9?\1fu\b1\0d\f55\d9?\af(\c5\d4\8cF\d8?\e03\d3<\c3<\d9?\b57\17\94\b9\a5\d8?\fb\ae7/J\03\d9?\da\17K\e9Ve\d8?1]\12y\85\bf\d9?b@n\n\9a\c1\d9?m\a9Z\fb\ec\93\d9?\83\fcz\91ur\d9?!\8aO\0b\18]\e0?\cea_w\1b+\e5?\a4\c0\f7\bcK8\e5?\fb\e9\12\f3i\bb\e7?\d9\b5\e9\\u\e0\e7?\af\10\ee#\92\"\eb?v\ecO\ddzo\eb?PJ]\f7\b1\d7\ee?\e8X\c0\124\f1\ef?\0b\a7\e62\83\01\f1?\d3\13\d6\00\cd\b1\f1?\a5\a8a\c0d\e9\f2?n\fd\08\d0\0c\1d\f4?\a4\87\11L\95)\f5?_\15\eb5C\fa\f5?\19\81j&\d7\ec\f7?\b2gk\aa\00\7f\f8?\d8\131\f6\07\e9\f9?\fc\d6\b0G\08Z\fb?;\f8\b7\0e\a0T\fc?\ddU\8ayQ\96\fd?\ff\ea\a7m\0e\c8\fe?U\12\b5\c1\ff\1c\00@*\d10\8e\0f\a1\00@^[I\c1\aaB\01@\0e\aa\dbH\d0\c1\01@pJ\ba\81\ca \02@\aa\a5\eb\c1\eb\90\02@m\t\11\01!\dc\02@\85\84\0d\e8[0\03@\ec\bb\85R\1f<\03@\f1\b1\c9B\a4l\03@z\80\93\a3L\"\03@\d2\7fnQ\dc9\03@$6\d7 /\e4\02@") - (data (i32.const 748) "\10\00\00\00\01\00\00\00\00\00\00\00\03\00\00\00\10\00\00\00\e0\00\00\00\e0\00\00\00\00\02\00\00@\00\00\00") - (data (i32.const 796) "\1c\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1c\00\00\00I\00n\00v\00a\00l\00i\00d\00 \00l\00e\00n\00g\00t\00h\00") - (data (i32.const 844) "&\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00&\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00b\00u\00f\00f\00e\00r\00.\00t\00s\00") - (data (i32.const 908) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00") - (data (i32.const 972) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00~\00l\00i\00b\00/\00t\00y\00p\00e\00d\00a\00r\00r\00a\00y\00.\00t\00s\00") - (data (i32.const 1032) "\00\00\00\00\00\a0\f6?\00\00\00\00\00\00\00\00\00\c8\b9\f2\82,\d6\bf\80V7($\b4\fa<\00\00\00\00\00\80\f6?\00\00\00\00\00\00\00\00\00\08X\bf\bd\d1\d5\bf \f7\e0\d8\08\a5\1c\bd\00\00\00\00\00`\f6?\00\00\00\00\00\00\00\00\00XE\17wv\d5\bfmP\b6\d5\a4b#\bd\00\00\00\00\00@\f6?\00\00\00\00\00\00\00\00\00\f8-\87\ad\1a\d5\bf\d5g\b0\9e\e4\84\e6\bc\00\00\00\00\00 \f6?\00\00\00\00\00\00\00\00\00xw\95_\be\d4\bf\e0>)\93i\1b\04\bd\00\00\00\00\00\00\f6?\00\00\00\00\00\00\00\00\00`\1c\c2\8ba\d4\bf\cc\84LH/\d8\13=\00\00\00\00\00\e0\f5?\00\00\00\00\00\00\00\00\00\a8\86\860\04\d4\bf:\0b\82\ed\f3B\dc<\00\00\00\00\00\c0\f5?\00\00\00\00\00\00\00\00\00HiUL\a6\d3\bf`\94Q\86\c6\b1 =\00\00\00\00\00\a0\f5?\00\00\00\00\00\00\00\00\00\80\98\9a\ddG\d3\bf\92\80\c5\d4MY%=\00\00\00\00\00\80\f5?\00\00\00\00\00\00\00\00\00 \e1\ba\e2\e8\d2\bf\d8+\b7\99\1e{&=\00\00\00\00\00`\f5?\00\00\00\00\00\00\00\00\00\88\de\13Z\89\d2\bf?\b0\cf\b6\14\ca\15=\00\00\00\00\00`\f5?\00\00\00\00\00\00\00\00\00\88\de\13Z\89\d2\bf?\b0\cf\b6\14\ca\15=\00\00\00\00\00@\f5?\00\00\00\00\00\00\00\00\00x\cf\fbA)\d2\bfv\daS($Z\16\bd\00\00\00\00\00 \f5?\00\00\00\00\00\00\00\00\00\98i\c1\98\c8\d1\bf\04T\e7h\bc\af\1f\bd\00\00\00\00\00\00\f5?\00\00\00\00\00\00\00\00\00\a8\ab\ab\\g\d1\bf\f0\a8\823\c6\1f\1f=\00\00\00\00\00\e0\f4?\00\00\00\00\00\00\00\00\00H\ae\f9\8b\05\d1\bffZ\05\fd\c4\a8&\bd\00\00\00\00\00\c0\f4?\00\00\00\00\00\00\00\00\00\90s\e2$\a3\d0\bf\0e\03\f4~\eek\0c\bd\00\00\00\00\00\a0\f4?\00\00\00\00\00\00\00\00\00\d0\b4\94%@\d0\bf\7f-\f4\9e\b86\f0\bc\00\00\00\00\00\a0\f4?\00\00\00\00\00\00\00\00\00\d0\b4\94%@\d0\bf\7f-\f4\9e\b86\f0\bc\00\00\00\00\00\80\f4?\00\00\00\00\00\00\00\00\00@^m\18\b9\cf\bf\87<\99\ab*W\0d=\00\00\00\00\00`\f4?\00\00\00\00\00\00\00\00\00`\dc\cb\ad\f0\ce\bf$\af\86\9c\b7&+=\00\00\00\00\00@\f4?\00\00\00\00\00\00\00\00\00\f0*n\07\'\ce\bf\10\ff?TO/\17\bd\00\00\00\00\00 \f4?\00\00\00\00\00\00\00\00\00\c0Ok!\\\cd\bf\1bh\ca\bb\91\ba!=\00\00\00\00\00\00\f4?\00\00\00\00\00\00\00\00\00\a0\9a\c7\f7\8f\cc\bf4\84\9fhOy\'=\00\00\00\00\00\00\f4?\00\00\00\00\00\00\00\00\00\a0\9a\c7\f7\8f\cc\bf4\84\9fhOy\'=\00\00\00\00\00\e0\f3?\00\00\00\00\00\00\00\00\00\90-t\86\c2\cb\bf\8f\b7\8b1\b0N\19=\00\00\00\00\00\c0\f3?\00\00\00\00\00\00\00\00\00\c0\80N\c9\f3\ca\bff\90\cd?cN\ba<\00\00\00\00\00\a0\f3?\00\00\00\00\00\00\00\00\00\b0\e2\1f\bc#\ca\bf\ea\c1F\dcd\8c%\bd\00\00\00\00\00\a0\f3?\00\00\00\00\00\00\00\00\00\b0\e2\1f\bc#\ca\bf\ea\c1F\dcd\8c%\bd\00\00\00\00\00\80\f3?\00\00\00\00\00\00\00\00\00P\f4\9cZR\c9\bf\e3\d4\c1\04\d9\d1*\bd\00\00\00\00\00`\f3?\00\00\00\00\00\00\00\00\00\d0 e\a0\7f\c8\bf\t\fa\db\7f\bf\bd+=\00\00\00\00\00@\f3?\00\00\00\00\00\00\00\00\00\e0\10\02\89\ab\c7\bfXJSr\90\db+=\00\00\00\00\00@\f3?\00\00\00\00\00\00\00\00\00\e0\10\02\89\ab\c7\bfXJSr\90\db+=\00\00\00\00\00 \f3?\00\00\00\00\00\00\00\00\00\d0\19\e7\0f\d6\c6\bff\e2\b2\a3j\e4\10\bd\00\00\00\00\00\00\f3?\00\00\00\00\00\00\00\00\00\90\a7p0\ff\c5\bf9P\10\9fC\9e\1e\bd\00\00\00\00\00\00\f3?\00\00\00\00\00\00\00\00\00\90\a7p0\ff\c5\bf9P\10\9fC\9e\1e\bd\00\00\00\00\00\e0\f2?\00\00\00\00\00\00\00\00\00\b0\a1\e3\e5&\c5\bf\8f[\07\90\8b\de \bd\00\00\00\00\00\c0\f2?\00\00\00\00\00\00\00\00\00\80\cbl+M\c4\bf\11\0e\bd\00\00\00\00\00\e0\ed?\00\00\00\00\00\00\00\00\00`F\d1;\97\b1?\9b\9e\0dV]2%\bd\00\00\00\00\00\a0\ed?\00\00\00\00\00\00\00\00\00\e0\d1\a7\f5\bd\b3?\d7N\db\a5^\c8,=\00\00\00\00\00`\ed?\00\00\00\00\00\00\00\00\00\a0\97MZ\e9\b5?\1e\1d]<\06i,\bd\00\00\00\00\00@\ed?\00\00\00\00\00\00\00\00\00\c0\ea\n\d3\00\b7?2\ed\9d\a9\8d\1e\ec<\00\00\00\00\00\00\ed?\00\00\00\00\00\00\00\00\00@Y]^3\b9?\daG\bd:\\\11#=\00\00\00\00\00\c0\ec?\00\00\00\00\00\00\00\00\00`\ad\8d\c8j\bb?\e5h\f7+\80\90\13\bd\00\00\00\00\00\a0\ec?\00\00\00\00\00\00\00\00\00@\bc\01X\88\bc?\d3\acZ\c6\d1F&=\00\00\00\00\00`\ec?\00\00\00\00\00\00\00\00\00 \n\839\c7\be?\e0E\e6\afh\c0-\bd\00\00\00\00\00@\ec?\00\00\00\00\00\00\00\00\00\e0\db9\91\e8\bf?\fd\n\a1O\d64%\bd\00\00\00\00\00\00\ec?\00\00\00\00\00\00\00\00\00\e0\'\82\8e\17\c1?\f2\07-\cex\ef!=\00\00\00\00\00\e0\eb?\00\00\00\00\00\00\00\00\00\f0#~+\aa\c1?4\998D\8e\a7,=\00\00\00\00\00\a0\eb?\00\00\00\00\00\00\00\00\00\80\86\0ca\d1\c2?\a1\b4\81\cbl\9d\03=\00\00\00\00\00\80\eb?\00\00\00\00\00\00\00\00\00\90\15\b0\fce\c3?\89rK#\a8/\c6<\00\00\00\00\00@\eb?\00\00\00\00\00\00\00\00\00\b03\83=\91\c4?x\b6\fdTy\83%=\00\00\00\00\00 \eb?\00\00\00\00\00\00\00\00\00\b0\a1\e4\e5\'\c5?\c7}i\e5\e83&=\00\00\00\00\00\e0\ea?\00\00\00\00\00\00\00\00\00\10\8c\beNW\c6?x.<,\8b\cf\19=\00\00\00\00\00\c0\ea?\00\00\00\00\00\00\00\00\00pu\8b\12\f0\c6?\e1!\9c\e5\8d\11%\bd\00\00\00\00\00\a0\ea?\00\00\00\00\00\00\00\00\00PD\85\8d\89\c7?\05C\91p\10f\1c\bd\00\00\00\00\00`\ea?\00\00\00\00\00\00\00\00\00\009\eb\af\be\c8?\d1,\e9\aaT=\07\bd\00\00\00\00\00@\ea?\00\00\00\00\00\00\00\00\00\00\f7\dcZZ\c9?o\ff\a0X(\f2\07=\00\00\00\00\00\00\ea?\00\00\00\00\00\00\00\00\00\e0\8a<\ed\93\ca?i!VPCr(\bd\00\00\00\00\00\e0\e9?\00\00\00\00\00\00\00\00\00\d0[W\d81\cb?\aa\e1\acN\8d5\0c\bd\00\00\00\00\00\c0\e9?\00\00\00\00\00\00\00\00\00\e0;8\87\d0\cb?\b6\12TY\c4K-\bd\00\00\00\00\00\a0\e9?\00\00\00\00\00\00\00\00\00\10\f0\c6\fbo\cc?\d2+\96\c5r\ec\f1\bc\00\00\00\00\00`\e9?\00\00\00\00\00\00\00\00\00\90\d4\b0=\b1\cd?5\b0\15\f7*\ff*\bd\00\00\00\00\00@\e9?\00\00\00\00\00\00\00\00\00\10\e7\ff\0eS\ce?0\f4A`\'\12\c2<\00\00\00\00\00 \e9?\00\00\00\00\00\00\00\00\00\00\dd\e4\ad\f5\ce?\11\8e\bbe\15!\ca\bc\00\00\00\00\00\00\e9?\00\00\00\00\00\00\00\00\00\b0\b3l\1c\99\cf?0\df\0c\ca\ec\cb\1b=\00\00\00\00\00\c0\e8?\00\00\00\00\00\00\00\00\00XM`8q\d0?\91N\ed\16\db\9c\f8<\00\00\00\00\00\a0\e8?\00\00\00\00\00\00\00\00\00`ag-\c4\d0?\e9\ea<\16\8b\18\'=\00\00\00\00\00\80\e8?\00\00\00\00\00\00\00\00\00\e8\'\82\8e\17\d1?\1c\f0\a5c\0e!,\bd\00\00\00\00\00`\e8?\00\00\00\00\00\00\00\00\00\f8\ac\cb\\k\d1?\81\16\a5\f7\cd\9a+=\00\00\00\00\00@\e8?\00\00\00\00\00\00\00\00\00hZc\99\bf\d1?\b7\bdGQ\ed\a6,=\00\00\00\00\00 \e8?\00\00\00\00\00\00\00\00\00\b8\0emE\14\d2?\ea\baF\ba\de\87\n=\00\00\00\00\00\e0\e7?\00\00\00\00\00\00\00\00\00\90\dc|\f0\be\d2?\f4\04PJ\fa\9c*=\00\00\00\00\00\c0\e7?\00\00\00\00\00\00\00\00\00`\d3\e1\f1\14\d3?\b8\9a\ec\ef?\d1f\87\10z^\90\bc\85\7fn\e8\15\e3\ef?\13\f6g5R\d2\8c\be\ef?m{\83]\a6\9a\97<\0f\89\f9lX\b5\ef?\fc\ef\fd\92\1a\b5\8e<\f7Gr+\92\ac\ef?\d1\9c/p=\be><\a2\d1\d32\ec\a3\ef?\0bn\90\894\03j\bc\1b\d3\fe\aff\9b\ef?\0e\bd/*RV\95\bcQ[\12\d0\01\93\ef?U\eaN\8c\ef\80P\bc\cc1l\c0\bd\8a\ef?\16\f4\d5\b9#\c9\91\bc\e0-\a9\ae\9a\82\ef?\afU\\\e9\e3\d3\80\f7\ec\9a<\aa\b9h1\87T\ef?\9d8\86\cb\82\e7\8f\bc\1d\d9\fc\"PM\ef?\8d\c3\a6DAo\8a<\d6\8cb\88;F\ef?}\04\e4\b0\05z\80<\96\dc}\91I?\ef?\94\a8\a8\e3\fd\8e\96<8bunz8\ef?}Ht\f2\18^\87\a9\af\0c\ef?\b6\ab\b0MuM\83<\15\b71\n\fe\06\ef?Lt\ac\e2\01B\86<1\d8L\fcp\01\ef?J\f8\d3]9\dd\8f<\ff\16d\b2\08\fc\ee?\04[\8e;\80\a3\86\bc\f1\9f\92_\c5\f6\ee?hPK\cc\edJ\92\bc\cb\a9:7\a7\f1\ee?\8e-Q\1b\f8\07\99\bcf\d8\05m\ae\ec\ee?\d26\94>\e8\d1q\bc\f7\9f\e54\db\e7\ee?\15\1b\ce\b3\19\19\99\bc\e5\a8\13\c3-\e3\ee?mL*\a7H\9f\85<\"4\12L\a6\de\ee?\8ai(z`\12\93\bc\1c\80\ac\04E\da\ee?[\89\17H\8f\a7X\bc*.\f7!\n\d6\ee?\1b\9aIg\9b,|\bc\97\a8P\d9\f5\d1\ee?\11\ac\c2`\edcC<-\89a`\08\ce\ee?\efd\06;\tf\96Z~d\1fx\bct_\ec\e8u\9f\ee?\b0}\8b\c0J\ee\86\bct\81\a5H\9a\9f\ee?\8a\e6U\1e2\19\86\bc\c9gBV\eb\9f\ee?\d3\d4\t^\cb\9c\90T\'\a4\ee?47;\f1\b6i\93\bc\13\ceL\99\89\a5\ee?\1e\ff\19:\84^\80\bc\ad\c7#F\1a\a7\ee?nWr\d8P\d4\94\bc\ed\92D\9b\d9\a8\ee?\00\8a\0e[g\ad\90<\99f\8a\d9\c7\aa\ee?\b4\ea\f0\c1/\b7\8d<\db\a0*B\e5\ac\ee?\ff\e7\c5\9c`\b6e\bc\8cD\b5\162\af\ee?D_\f3Y\83\f6{<6w\15\99\ae\b1\ee?\83=\1e\a7\1f\t\93\bc\c6\ff\91\0b[\b4\ee?)\1el\8b\b8\a9]\bc\e5\c5\cd\b07\b7\ee?Y\b9\90|\f9#l\bc\0fR\c8\cbD\ba\ee?\aa\f9\f4\"CC\92\bcPN\de\9f\82\bd\ee?K\8ef\d7l\ca\85\bc\ba\07\cap\f1\c0\ee?\'\ce\91+\fc\afq<\90\f0\a3\82\91\c4\ee?\bbs\n\e15\d2m<##\e3\19c\c8\ee?c\"b\"\04\c5\87\bce\e5]{f\cc\ee?\d51\e2\e3\86\1c\8b<3-J\ec\9b\d0\ee?\15\bb\bc\d3\d1\bb\91\bc]%>\b2\03\d5\ee?\d21\ee\9c1\cc\90\b4\07!\d5\82\bc_\9b{3\97|\ef?\c9\0dG;\b9*\89\bc)\a1\f5\14F\86\ef?\d3\88:`\04\b6t<\f6?\8b\e7.\90\ef?qr\9dQ\ec\c5\83<\83L\c7\fbQ\9a\ef?\f0\91\d3\8f\12\f7\8f\bc\da\90\a4\a2\af\a4\ef?}t#\e2\98\ae\8d\bc\f1g\8e-H\af\ef?\08 \aaA\bc\c3\8e<\'Za\ee\1b\ba\ef?2\eb\a9\c3\94+\84<\97\bak7+\c5\ef?\ee\85\d11\a9d\8a<@En[v\d0\ef?\ed\e3;\e4\ba7\8e\bc\14\be\9c\ad\fd\db\ef?\9d\cd\91M;\89w<\d8\90\9e\81\c1\e7\ef?\89\cc`A\c1\05S<\f1q\8f+\c2\f3\ef?") - (data (i32.const 7184) "\08\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\"\1a\00\00\00\00\00\00!\1a\00\00\02\00\00\00a\00\00\00\02\00\00\00!\01\00\00\02\00\00\00\"\t\00\00\00\00\00\00") - (table $0 1 funcref) - (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) - (global $~lib/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0)) - (global $~lib/ASC_SHRINK_LEVEL i32 (i32.const 0)) - (global $assembly/index/DAT_LEN i32 (i32.const 128)) - (global $assembly/index/HLF_LEN i32 (i32.const 64)) - (global $assembly/index/QRT_LEN i32 (i32.const 32)) - (global $assembly/index/pNoise i32 (i32.const 768)) - (global $assembly/index/pinkNoise (mut i32) (i32.const 0)) - (global $assembly/index/inputData (mut i32) (i32.const 0)) - (global $~lib/builtins/i32.MAX_VALUE i32 (i32.const 2147483647)) - (global $assembly/index/outputData (mut i32) (i32.const 0)) - (global $assembly/index/audioProps (mut i32) (i32.const 0)) - (global $assembly/index/audioSettings (mut i32) (i32.const 0)) - (global $~lib/util/math/log_tail (mut f64) (f64.const 0)) - (global $~lib/rt/__rtti_base i32 (i32.const 7184)) - (global $~lib/memory/__heap_base i32 (i32.const 7252)) - (export "memory" (memory $0)) - (export "__new" (func $~lib/rt/pure/__new)) - (export "__renew" (func $~lib/rt/pure/__renew)) - (export "__retain" (func $~lib/rt/pure/__retain)) - (export "__release" (func $~lib/rt/pure/__release)) - (export "__rtti_base" (global $~lib/rt/__rtti_base)) - (export "allocF64Array" (func $assembly/index/allocF64Array)) - (export "allocU32Array" (func $assembly/index/allocU32Array)) - (export "inputData" (global $assembly/index/inputData)) - (export "outputData" (global $assembly/index/outputData)) - (export "audioProps" (global $assembly/index/audioProps)) - (export "audioSettings" (global $assembly/index/audioSettings)) - (export "update" (func $assembly/index/update)) - (start $~start) - (func $~lib/rt/tlsf/removeBlock (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - local.get $1 - i32.load - local.set $2 - i32.const 1 - drop - local.get $2 - i32.const 1 - i32.and - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 272 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $2 - i32.const 3 - i32.const -1 - i32.xor - i32.and - local.set $3 - i32.const 1 - drop - local.get $3 - i32.const 12 - i32.ge_u - if (result i32) - local.get $3 - i32.const 1073741820 - i32.lt_u - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 274 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $3 - i32.const 256 - i32.lt_u - if - i32.const 0 - local.set $4 - local.get $3 - i32.const 4 - i32.shr_u - local.set $5 - else - i32.const 31 - local.get $3 - i32.clz - i32.sub - local.set $4 - local.get $3 - local.get $4 - i32.const 4 - i32.sub - i32.shr_u - i32.const 1 - i32.const 4 - i32.shl - i32.xor - local.set $5 - local.get $4 - i32.const 8 - i32.const 1 - i32.sub - i32.sub - local.set $4 - end - i32.const 1 - drop - local.get $4 - i32.const 23 - i32.lt_u - if (result i32) - local.get $5 - i32.const 16 - i32.lt_u - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 287 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.load offset=4 - local.set $6 - local.get $1 - i32.load offset=8 - local.set $7 - local.get $6 - if - local.get $6 - local.get $7 - i32.store offset=8 - end - local.get $7 - if - local.get $7 - local.get $6 - i32.store offset=4 - end - local.get $1 - local.get $0 - local.set $10 - local.get $4 - local.set $9 - local.get $5 - local.set $8 - local.get $10 - local.get $9 - i32.const 4 - i32.shl - local.get $8 - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - i32.eq - if - local.get $0 - local.set $11 - local.get $4 - local.set $10 - local.get $5 - local.set $9 - local.get $7 - local.set $8 - local.get $11 - local.get $10 - i32.const 4 - i32.shl - local.get $9 - i32.add - i32.const 2 - i32.shl - i32.add - local.get $8 - i32.store offset=96 - local.get $7 - i32.eqz - if - local.get $0 - local.set $9 - local.get $4 - local.set $8 - local.get $9 - local.get $8 - i32.const 2 - i32.shl - i32.add - i32.load offset=4 - local.set $9 - local.get $0 - local.set $8 - local.get $4 - local.set $11 - local.get $9 - i32.const 1 - local.get $5 - i32.shl - i32.const -1 - i32.xor - i32.and - local.tee $9 - local.set $10 - local.get $8 - local.get $11 - i32.const 2 - i32.shl - i32.add - local.get $10 - i32.store offset=4 - local.get $9 - i32.eqz - if - local.get $0 - local.get $0 - i32.load - i32.const 1 - local.get $4 - i32.shl - i32.const -1 - i32.xor - i32.and - i32.store - end - end - end - ) - (func $~lib/rt/tlsf/insertBlock (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - (local $13 i32) - i32.const 1 - drop - local.get $1 - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 200 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.load - local.set $2 - i32.const 1 - drop - local.get $2 - i32.const 1 - i32.and - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 202 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - local.set $3 - local.get $3 - i32.const 4 - i32.add - local.get $3 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.set $4 - local.get $4 - i32.load - local.set $5 - local.get $5 - i32.const 1 - i32.and - if - local.get $2 - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.const 4 - i32.add - local.get $5 - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.set $3 - local.get $3 - i32.const 1073741820 - i32.lt_u - if - local.get $0 - local.get $4 - call $~lib/rt/tlsf/removeBlock - local.get $1 - local.get $2 - i32.const 3 - i32.and - local.get $3 - i32.or - local.tee $2 - i32.store - local.get $1 - local.set $6 - local.get $6 - i32.const 4 - i32.add - local.get $6 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.set $4 - local.get $4 - i32.load - local.set $5 - end - end - local.get $2 - i32.const 2 - i32.and - if - local.get $1 - local.set $6 - local.get $6 - i32.const 4 - i32.sub - i32.load - local.set $6 - local.get $6 - i32.load - local.set $3 - i32.const 1 - drop - local.get $3 - i32.const 1 - i32.and - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 223 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $3 - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.const 4 - i32.add - local.get $2 - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.set $7 - local.get $7 - i32.const 1073741820 - i32.lt_u - if - local.get $0 - local.get $6 - call $~lib/rt/tlsf/removeBlock - local.get $6 - local.get $3 - i32.const 3 - i32.and - local.get $7 - i32.or - local.tee $2 - i32.store - local.get $6 - local.set $1 - end - end - local.get $4 - local.get $5 - i32.const 2 - i32.or - i32.store - local.get $2 - i32.const 3 - i32.const -1 - i32.xor - i32.and - local.set $8 - i32.const 1 - drop - local.get $8 - i32.const 12 - i32.ge_u - if (result i32) - local.get $8 - i32.const 1073741820 - i32.lt_u - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 238 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - i32.const 1 - drop - local.get $1 - i32.const 4 - i32.add - local.get $8 - i32.add - local.get $4 - i32.eq - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 239 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $4 - i32.const 4 - i32.sub - local.get $1 - i32.store - local.get $8 - i32.const 256 - i32.lt_u - if - i32.const 0 - local.set $9 - local.get $8 - i32.const 4 - i32.shr_u - local.set $10 - else - i32.const 31 - local.get $8 - i32.clz - i32.sub - local.set $9 - local.get $8 - local.get $9 - i32.const 4 - i32.sub - i32.shr_u - i32.const 1 - i32.const 4 - i32.shl - i32.xor - local.set $10 - local.get $9 - i32.const 8 - i32.const 1 - i32.sub - i32.sub - local.set $9 - end - i32.const 1 - drop - local.get $9 - i32.const 23 - i32.lt_u - if (result i32) - local.get $10 - i32.const 16 - i32.lt_u - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 255 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.set $7 - local.get $9 - local.set $3 - local.get $10 - local.set $6 - local.get $7 - local.get $3 - i32.const 4 - i32.shl - local.get $6 - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - local.set $11 - local.get $1 - i32.const 0 - i32.store offset=4 - local.get $1 - local.get $11 - i32.store offset=8 - local.get $11 - if - local.get $11 - local.get $1 - i32.store offset=4 - end - local.get $0 - local.set $12 - local.get $9 - local.set $7 - local.get $10 - local.set $3 - local.get $1 - local.set $6 - local.get $12 - local.get $7 - i32.const 4 - i32.shl - local.get $3 - i32.add - i32.const 2 - i32.shl - i32.add - local.get $6 - i32.store offset=96 - local.get $0 - local.get $0 - i32.load - i32.const 1 - local.get $9 - i32.shl - i32.or - i32.store - local.get $0 - local.set $13 - local.get $9 - local.set $12 - local.get $0 - local.set $3 - local.get $9 - local.set $6 - local.get $3 - local.get $6 - i32.const 2 - i32.shl - i32.add - i32.load offset=4 - i32.const 1 - local.get $10 - i32.shl - i32.or - local.set $7 - local.get $13 - local.get $12 - i32.const 2 - i32.shl - i32.add - local.get $7 - i32.store offset=4 - ) - (func $~lib/rt/tlsf/addMemory (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - i32.const 1 - drop - local.get $1 - local.get $2 - i32.le_u - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 380 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.const 4 - i32.add - i32.const 15 - i32.add - i32.const 15 - i32.const -1 - i32.xor - i32.and - i32.const 4 - i32.sub - local.set $1 - local.get $2 - i32.const 15 - i32.const -1 - i32.xor - i32.and - local.set $2 - local.get $0 - local.set $3 - local.get $3 - i32.load offset=1568 - local.set $4 - i32.const 0 - local.set $5 - local.get $4 - if - i32.const 1 - drop - local.get $1 - local.get $4 - i32.const 4 - i32.add - i32.ge_u - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 387 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $1 - i32.const 16 - i32.sub - local.get $4 - i32.eq - if - local.get $1 - i32.const 16 - i32.sub - local.set $1 - local.get $4 - i32.load - local.set $5 - else - nop - end - else - i32.const 1 - drop - local.get $1 - local.get $0 - i32.const 1572 - i32.add - i32.ge_u - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 400 - i32.const 5 - call $~lib/builtins/abort - unreachable - end - end - local.get $2 - local.get $1 - i32.sub - local.set $6 - local.get $6 - i32.const 4 - i32.const 12 - i32.add - i32.const 4 - i32.add - i32.lt_u - if - i32.const 0 - return - end - local.get $6 - i32.const 2 - i32.const 4 - i32.mul - i32.sub - local.set $7 - local.get $1 - local.set $8 - local.get $8 - local.get $7 - i32.const 1 - i32.or - local.get $5 - i32.const 2 - i32.and - i32.or - i32.store - local.get $8 - i32.const 0 - i32.store offset=4 - local.get $8 - i32.const 0 - i32.store offset=8 - local.get $1 - i32.const 4 - i32.add - local.get $7 - i32.add - local.set $4 - local.get $4 - i32.const 0 - i32.const 2 - i32.or - i32.store - local.get $0 - local.set $9 - local.get $4 - local.set $3 - local.get $9 - local.get $3 - i32.store offset=1568 - local.get $0 - local.get $8 - call $~lib/rt/tlsf/insertBlock - i32.const 1 - ) - (func $~lib/rt/tlsf/initialize - (local $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - (local $12 i32) - global.get $~lib/memory/__heap_base - i32.const 15 - i32.add - i32.const 15 - i32.const -1 - i32.xor - i32.and - local.set $0 - memory.size - local.set $1 - local.get $0 - i32.const 1572 - i32.add - i32.const 65535 - i32.add - i32.const 65535 - i32.const -1 - i32.xor - i32.and - i32.const 16 - i32.shr_u - local.set $2 - local.get $2 - local.get $1 - i32.gt_s - if (result i32) - local.get $2 - local.get $1 - i32.sub - memory.grow - i32.const 0 - i32.lt_s - else - i32.const 0 - end - if - unreachable - end - local.get $0 - local.set $3 - local.get $3 - i32.const 0 - i32.store - local.get $3 - local.set $5 - i32.const 0 - local.set $4 - local.get $5 - local.get $4 - i32.store offset=1568 - i32.const 0 - local.set $5 - loop $for-loop|0 - local.get $5 - i32.const 23 - i32.lt_u - local.set $4 - local.get $4 - if - local.get $3 - local.set $8 - local.get $5 - local.set $7 - i32.const 0 - local.set $6 - local.get $8 - local.get $7 - i32.const 2 - i32.shl - i32.add - local.get $6 - i32.store offset=4 - i32.const 0 - local.set $8 - loop $for-loop|1 - local.get $8 - i32.const 16 - i32.lt_u - local.set $7 - local.get $7 - if - local.get $3 - local.set $11 - local.get $5 - local.set $10 - local.get $8 - local.set $9 - i32.const 0 - local.set $6 - local.get $11 - local.get $10 - i32.const 4 - i32.shl - local.get $9 - i32.add - i32.const 2 - i32.shl - i32.add - local.get $6 - i32.store offset=96 - local.get $8 - i32.const 1 - i32.add - local.set $8 - br $for-loop|1 - end - end - local.get $5 - i32.const 1 - i32.add - local.set $5 - br $for-loop|0 - end - end - local.get $0 - i32.const 1572 - i32.add - local.set $12 - i32.const 0 - drop - local.get $3 - local.get $12 - memory.size - i32.const 16 - i32.shl - call $~lib/rt/tlsf/addMemory - drop - local.get $3 - global.set $~lib/rt/tlsf/ROOT - ) - (func $~lib/rt/tlsf/computeSize (param $0 i32) (result i32) - local.get $0 - i32.const 12 - i32.le_u - if (result i32) - i32.const 12 - else - local.get $0 - i32.const 4 - i32.add - i32.const 15 - i32.add - i32.const 15 - i32.const -1 - i32.xor - i32.and - i32.const 4 - i32.sub - end - ) - (func $~lib/rt/tlsf/prepareSize (param $0 i32) (result i32) - local.get $0 - i32.const 1073741820 - i32.ge_u - if - i32.const 32 - i32.const 160 - i32.const 461 - i32.const 30 - call $~lib/builtins/abort - unreachable - end - local.get $0 - call $~lib/rt/tlsf/computeSize - ) - (func $~lib/rt/tlsf/searchBlock (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - local.get $1 - i32.const 256 - i32.lt_u - if - i32.const 0 - local.set $2 - local.get $1 - i32.const 4 - i32.shr_u - local.set $3 - else - local.get $1 - i32.const 536870910 - i32.lt_u - if (result i32) - local.get $1 - i32.const 1 - i32.const 27 - local.get $1 - i32.clz - i32.sub - i32.shl - i32.add - i32.const 1 - i32.sub - else - local.get $1 - end - local.set $4 - i32.const 31 - local.get $4 - i32.clz - i32.sub - local.set $2 - local.get $4 - local.get $2 - i32.const 4 - i32.sub - i32.shr_u - i32.const 1 - i32.const 4 - i32.shl - i32.xor - local.set $3 - local.get $2 - i32.const 8 - i32.const 1 - i32.sub - i32.sub - local.set $2 - end - i32.const 1 - drop - local.get $2 - i32.const 23 - i32.lt_u - if (result i32) - local.get $3 - i32.const 16 - i32.lt_u - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 333 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.set $5 - local.get $2 - local.set $4 - local.get $5 - local.get $4 - i32.const 2 - i32.shl - i32.add - i32.load offset=4 - i32.const 0 - i32.const -1 - i32.xor - local.get $3 - i32.shl - i32.and - local.set $6 - i32.const 0 - local.set $7 - local.get $6 - i32.eqz - if - local.get $0 - i32.load - i32.const 0 - i32.const -1 - i32.xor - local.get $2 - i32.const 1 - i32.add - i32.shl - i32.and - local.set $5 - local.get $5 - i32.eqz - if - i32.const 0 - local.set $7 - else - local.get $5 - i32.ctz - local.set $2 - local.get $0 - local.set $8 - local.get $2 - local.set $4 - local.get $8 - local.get $4 - i32.const 2 - i32.shl - i32.add - i32.load offset=4 - local.set $6 - i32.const 1 - drop - local.get $6 - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 346 - i32.const 18 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.set $9 - local.get $2 - local.set $8 - local.get $6 - i32.ctz - local.set $4 - local.get $9 - local.get $8 - i32.const 4 - i32.shl - local.get $4 - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - local.set $7 - end - else - local.get $0 - local.set $9 - local.get $2 - local.set $8 - local.get $6 - i32.ctz - local.set $4 - local.get $9 - local.get $8 - i32.const 4 - i32.shl - local.get $4 - i32.add - i32.const 2 - i32.shl - i32.add - i32.load offset=96 - local.set $7 - end - local.get $7 - ) - (func $~lib/rt/tlsf/growMemory (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - i32.const 0 - drop - local.get $1 - i32.const 536870910 - i32.lt_u - if - local.get $1 - i32.const 1 - i32.const 27 - local.get $1 - i32.clz - i32.sub - i32.shl - i32.const 1 - i32.sub - i32.add - local.set $1 - end - memory.size - local.set $2 - local.get $1 - i32.const 4 - local.get $2 - i32.const 16 - i32.shl - i32.const 4 - i32.sub - local.get $0 - local.set $3 - local.get $3 - i32.load offset=1568 - i32.ne - i32.shl - i32.add - local.set $1 - local.get $1 - i32.const 65535 - i32.add - i32.const 65535 - i32.const -1 - i32.xor - i32.and - i32.const 16 - i32.shr_u - local.set $4 - local.get $2 - local.tee $3 - local.get $4 - local.tee $5 - local.get $3 - local.get $5 - i32.gt_s - select - local.set $6 - local.get $6 - memory.grow - i32.const 0 - i32.lt_s - if - local.get $4 - memory.grow - i32.const 0 - i32.lt_s - if - unreachable - end - end - memory.size - local.set $7 - local.get $0 - local.get $2 - i32.const 16 - i32.shl - local.get $7 - i32.const 16 - i32.shl - call $~lib/rt/tlsf/addMemory - drop - ) - (func $~lib/rt/tlsf/prepareBlock (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - local.get $1 - i32.load - local.set $3 - i32.const 1 - drop - local.get $2 - i32.const 4 - i32.add - i32.const 15 - i32.and - i32.eqz - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 360 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $3 - i32.const 3 - i32.const -1 - i32.xor - i32.and - local.get $2 - i32.sub - local.set $4 - local.get $4 - i32.const 4 - i32.const 12 - i32.add - i32.ge_u - if - local.get $1 - local.get $2 - local.get $3 - i32.const 2 - i32.and - i32.or - i32.store - local.get $1 - i32.const 4 - i32.add - local.get $2 - i32.add - local.set $5 - local.get $5 - local.get $4 - i32.const 4 - i32.sub - i32.const 1 - i32.or - i32.store - local.get $0 - local.get $5 - call $~lib/rt/tlsf/insertBlock - else - local.get $1 - local.get $3 - i32.const 1 - i32.const -1 - i32.xor - i32.and - i32.store - local.get $1 - local.set $5 - local.get $5 - i32.const 4 - i32.add - local.get $5 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.get $1 - local.set $5 - local.get $5 - i32.const 4 - i32.add - local.get $5 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - i32.load - i32.const 2 - i32.const -1 - i32.xor - i32.and - i32.store - end - ) - (func $~lib/rt/tlsf/allocateBlock (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - local.get $1 - call $~lib/rt/tlsf/prepareSize - local.set $2 - local.get $0 - local.get $2 - call $~lib/rt/tlsf/searchBlock - local.set $3 - local.get $3 - i32.eqz - if - local.get $0 - local.get $2 - call $~lib/rt/tlsf/growMemory - local.get $0 - local.get $2 - call $~lib/rt/tlsf/searchBlock - local.set $3 - i32.const 1 - drop - local.get $3 - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 498 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - end - i32.const 1 - drop - local.get $3 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - local.get $2 - i32.ge_u - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 500 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $3 - call $~lib/rt/tlsf/removeBlock - local.get $0 - local.get $3 - local.get $2 - call $~lib/rt/tlsf/prepareBlock - i32.const 0 - drop - local.get $3 - ) - (func $~lib/rt/tlsf/__alloc (param $0 i32) (result i32) - global.get $~lib/rt/tlsf/ROOT - i32.eqz - if - call $~lib/rt/tlsf/initialize - end - global.get $~lib/rt/tlsf/ROOT - local.get $0 - call $~lib/rt/tlsf/allocateBlock - i32.const 4 - i32.add - ) - (func $~lib/rt/pure/__new (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - (local $3 i32) - local.get $0 - i32.const 1073741804 - i32.gt_u - if - i32.const 32 - i32.const 96 - i32.const 275 - i32.const 30 - call $~lib/builtins/abort - unreachable - end - i32.const 16 - local.get $0 - i32.add - call $~lib/rt/tlsf/__alloc - local.set $2 - local.get $2 - i32.const 4 - i32.sub - local.set $3 - local.get $3 - i32.const 0 - i32.store offset=4 - local.get $3 - i32.const 0 - i32.store offset=8 - local.get $3 - local.get $1 - i32.store offset=12 - local.get $3 - local.get $0 - i32.store offset=16 - local.get $2 - i32.const 16 - i32.add - ) - (func $~lib/rt/tlsf/checkUsedBlock (param $0 i32) (result i32) - (local $1 i32) - local.get $0 - i32.const 4 - i32.sub - local.set $1 - local.get $0 - i32.const 0 - i32.ne - if (result i32) - local.get $0 - i32.const 15 - i32.and - i32.eqz - else - i32.const 0 - end - if (result i32) - local.get $1 - i32.load - i32.const 1 - i32.and - i32.eqz - else - i32.const 0 - end - i32.eqz - if - i32.const 0 - i32.const 160 - i32.const 563 - i32.const 3 - call $~lib/builtins/abort - unreachable - end - local.get $1 - ) - (func $~lib/util/memory/memcpy (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - loop $while-continue|0 - local.get $2 - if (result i32) - local.get $1 - i32.const 3 - i32.and - else - i32.const 0 - end - local.set $5 - local.get $5 - if - local.get $0 - local.tee $6 - i32.const 1 - i32.add - local.set $0 - local.get $6 - local.get $1 - local.tee $6 - i32.const 1 - i32.add - local.set $1 - local.get $6 - i32.load8_u - i32.store8 - local.get $2 - i32.const 1 - i32.sub - local.set $2 - br $while-continue|0 - end - end - local.get $0 - i32.const 3 - i32.and - i32.const 0 - i32.eq - if - loop $while-continue|1 - local.get $2 - i32.const 16 - i32.ge_u - local.set $5 - local.get $5 - if - local.get $0 - local.get $1 - i32.load - i32.store - local.get $0 - i32.const 4 - i32.add - local.get $1 - i32.const 4 - i32.add - i32.load - i32.store - local.get $0 - i32.const 8 - i32.add - local.get $1 - i32.const 8 - i32.add - i32.load - i32.store - local.get $0 - i32.const 12 - i32.add - local.get $1 - i32.const 12 - i32.add - i32.load - i32.store - local.get $1 - i32.const 16 - i32.add - local.set $1 - local.get $0 - i32.const 16 - i32.add - local.set $0 - local.get $2 - i32.const 16 - i32.sub - local.set $2 - br $while-continue|1 - end - end - local.get $2 - i32.const 8 - i32.and - if - local.get $0 - local.get $1 - i32.load - i32.store - local.get $0 - i32.const 4 - i32.add - local.get $1 - i32.const 4 - i32.add - i32.load - i32.store - local.get $0 - i32.const 8 - i32.add - local.set $0 - local.get $1 - i32.const 8 - i32.add - local.set $1 - end - local.get $2 - i32.const 4 - i32.and - if - local.get $0 - local.get $1 - i32.load - i32.store - local.get $0 - i32.const 4 - i32.add - local.set $0 - local.get $1 - i32.const 4 - i32.add - local.set $1 - end - local.get $2 - i32.const 2 - i32.and - if - local.get $0 - local.get $1 - i32.load16_u - i32.store16 - local.get $0 - i32.const 2 - i32.add - local.set $0 - local.get $1 - i32.const 2 - i32.add - local.set $1 - end - local.get $2 - i32.const 1 - i32.and - if - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - end - return - end - local.get $2 - i32.const 32 - i32.ge_u - if - block $break|2 - block $case2|2 - block $case1|2 - block $case0|2 - local.get $0 - i32.const 3 - i32.and - local.set $5 - local.get $5 - i32.const 1 - i32.eq - br_if $case0|2 - local.get $5 - i32.const 2 - i32.eq - br_if $case1|2 - local.get $5 - i32.const 3 - i32.eq - br_if $case2|2 - br $break|2 - end - local.get $1 - i32.load - local.set $3 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $2 - i32.const 3 - i32.sub - local.set $2 - loop $while-continue|3 - local.get $2 - i32.const 17 - i32.ge_u - local.set $5 - local.get $5 - if - local.get $1 - i32.const 1 - i32.add - i32.load - local.set $4 - local.get $0 - local.get $3 - i32.const 24 - i32.shr_u - local.get $4 - i32.const 8 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 5 - i32.add - i32.load - local.set $3 - local.get $0 - i32.const 4 - i32.add - local.get $4 - i32.const 24 - i32.shr_u - local.get $3 - i32.const 8 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 9 - i32.add - i32.load - local.set $4 - local.get $0 - i32.const 8 - i32.add - local.get $3 - i32.const 24 - i32.shr_u - local.get $4 - i32.const 8 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 13 - i32.add - i32.load - local.set $3 - local.get $0 - i32.const 12 - i32.add - local.get $4 - i32.const 24 - i32.shr_u - local.get $3 - i32.const 8 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 16 - i32.add - local.set $1 - local.get $0 - i32.const 16 - i32.add - local.set $0 - local.get $2 - i32.const 16 - i32.sub - local.set $2 - br $while-continue|3 - end - end - br $break|2 - end - local.get $1 - i32.load - local.set $3 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $2 - i32.const 2 - i32.sub - local.set $2 - loop $while-continue|4 - local.get $2 - i32.const 18 - i32.ge_u - local.set $5 - local.get $5 - if - local.get $1 - i32.const 2 - i32.add - i32.load - local.set $4 - local.get $0 - local.get $3 - i32.const 16 - i32.shr_u - local.get $4 - i32.const 16 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 6 - i32.add - i32.load - local.set $3 - local.get $0 - i32.const 4 - i32.add - local.get $4 - i32.const 16 - i32.shr_u - local.get $3 - i32.const 16 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 10 - i32.add - i32.load - local.set $4 - local.get $0 - i32.const 8 - i32.add - local.get $3 - i32.const 16 - i32.shr_u - local.get $4 - i32.const 16 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 14 - i32.add - i32.load - local.set $3 - local.get $0 - i32.const 12 - i32.add - local.get $4 - i32.const 16 - i32.shr_u - local.get $3 - i32.const 16 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 16 - i32.add - local.set $1 - local.get $0 - i32.const 16 - i32.add - local.set $0 - local.get $2 - i32.const 16 - i32.sub - local.set $2 - br $while-continue|4 - end - end - br $break|2 - end - local.get $1 - i32.load - local.set $3 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $2 - i32.const 1 - i32.sub - local.set $2 - loop $while-continue|5 - local.get $2 - i32.const 19 - i32.ge_u - local.set $5 - local.get $5 - if - local.get $1 - i32.const 3 - i32.add - i32.load - local.set $4 - local.get $0 - local.get $3 - i32.const 8 - i32.shr_u - local.get $4 - i32.const 24 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 7 - i32.add - i32.load - local.set $3 - local.get $0 - i32.const 4 - i32.add - local.get $4 - i32.const 8 - i32.shr_u - local.get $3 - i32.const 24 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 11 - i32.add - i32.load - local.set $4 - local.get $0 - i32.const 8 - i32.add - local.get $3 - i32.const 8 - i32.shr_u - local.get $4 - i32.const 24 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 15 - i32.add - i32.load - local.set $3 - local.get $0 - i32.const 12 - i32.add - local.get $4 - i32.const 8 - i32.shr_u - local.get $3 - i32.const 24 - i32.shl - i32.or - i32.store - local.get $1 - i32.const 16 - i32.add - local.set $1 - local.get $0 - i32.const 16 - i32.add - local.set $0 - local.get $2 - i32.const 16 - i32.sub - local.set $2 - br $while-continue|5 - end - end - br $break|2 - end - end - local.get $2 - i32.const 16 - i32.and - if - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - end - local.get $2 - i32.const 8 - i32.and - if - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - end - local.get $2 - i32.const 4 - i32.and - if - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - end - local.get $2 - i32.const 2 - i32.and - if - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - end - local.get $2 - i32.const 1 - i32.and - if - local.get $0 - local.tee $5 - i32.const 1 - i32.add - local.set $0 - local.get $5 - local.get $1 - local.tee $5 - i32.const 1 - i32.add - local.set $1 - local.get $5 - i32.load8_u - i32.store8 - end - ) - (func $~lib/memory/memory.copy (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - block $~lib/util/memory/memmove|inlined.0 - local.get $0 - local.set $5 - local.get $1 - local.set $4 - local.get $2 - local.set $3 - local.get $5 - local.get $4 - i32.eq - if - br $~lib/util/memory/memmove|inlined.0 - end - i32.const 0 - i32.const 1 - i32.lt_s - drop - local.get $4 - local.get $5 - i32.sub - local.get $3 - i32.sub - i32.const 0 - local.get $3 - i32.const 1 - i32.shl - i32.sub - i32.le_u - if - local.get $5 - local.get $4 - local.get $3 - call $~lib/util/memory/memcpy - br $~lib/util/memory/memmove|inlined.0 - end - local.get $5 - local.get $4 - i32.lt_u - if - i32.const 0 - i32.const 2 - i32.lt_s - drop - local.get $4 - i32.const 7 - i32.and - local.get $5 - i32.const 7 - i32.and - i32.eq - if - loop $while-continue|0 - local.get $5 - i32.const 7 - i32.and - local.set $6 - local.get $6 - if - local.get $3 - i32.eqz - if - br $~lib/util/memory/memmove|inlined.0 - end - local.get $3 - i32.const 1 - i32.sub - local.set $3 - local.get $5 - local.tee $7 - i32.const 1 - i32.add - local.set $5 - local.get $7 - local.get $4 - local.tee $7 - i32.const 1 - i32.add - local.set $4 - local.get $7 - i32.load8_u - i32.store8 - br $while-continue|0 - end - end - loop $while-continue|1 - local.get $3 - i32.const 8 - i32.ge_u - local.set $6 - local.get $6 - if - local.get $5 - local.get $4 - i64.load - i64.store - local.get $3 - i32.const 8 - i32.sub - local.set $3 - local.get $5 - i32.const 8 - i32.add - local.set $5 - local.get $4 - i32.const 8 - i32.add - local.set $4 - br $while-continue|1 - end - end - end - loop $while-continue|2 - local.get $3 - local.set $6 - local.get $6 - if - local.get $5 - local.tee $7 - i32.const 1 - i32.add - local.set $5 - local.get $7 - local.get $4 - local.tee $7 - i32.const 1 - i32.add - local.set $4 - local.get $7 - i32.load8_u - i32.store8 - local.get $3 - i32.const 1 - i32.sub - local.set $3 - br $while-continue|2 - end - end - else - i32.const 0 - i32.const 2 - i32.lt_s - drop - local.get $4 - i32.const 7 - i32.and - local.get $5 - i32.const 7 - i32.and - i32.eq - if - loop $while-continue|3 - local.get $5 - local.get $3 - i32.add - i32.const 7 - i32.and - local.set $6 - local.get $6 - if - local.get $3 - i32.eqz - if - br $~lib/util/memory/memmove|inlined.0 - end - local.get $5 - local.get $3 - i32.const 1 - i32.sub - local.tee $3 - i32.add - local.get $4 - local.get $3 - i32.add - i32.load8_u - i32.store8 - br $while-continue|3 - end - end - loop $while-continue|4 - local.get $3 - i32.const 8 - i32.ge_u - local.set $6 - local.get $6 - if - local.get $3 - i32.const 8 - i32.sub - local.set $3 - local.get $5 - local.get $3 - i32.add - local.get $4 - local.get $3 - i32.add - i64.load - i64.store - br $while-continue|4 - end - end - end - loop $while-continue|5 - local.get $3 - local.set $6 - local.get $6 - if - local.get $5 - local.get $3 - i32.const 1 - i32.sub - local.tee $3 - i32.add - local.get $4 - local.get $3 - i32.add - i32.load8_u - i32.store8 - br $while-continue|5 - end - end - end - end - ) - (func $~lib/rt/tlsf/freeBlock (param $0 i32) (param $1 i32) - local.get $1 - local.get $1 - i32.load - i32.const 1 - i32.or - i32.store - i32.const 0 - drop - local.get $0 - local.get $1 - call $~lib/rt/tlsf/insertBlock - ) - (func $~lib/rt/tlsf/moveBlock (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - local.get $0 - local.get $2 - call $~lib/rt/tlsf/allocateBlock - local.set $3 - local.get $3 - i32.const 4 - i32.add - local.get $1 - i32.const 4 - i32.add - local.get $1 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - call $~lib/memory/memory.copy - local.get $1 - global.get $~lib/memory/__heap_base - i32.ge_u - if - i32.const 0 - drop - local.get $0 - local.get $1 - call $~lib/rt/tlsf/freeBlock - end - local.get $3 - ) - (func $~lib/rt/tlsf/reallocateBlock (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - local.get $2 - call $~lib/rt/tlsf/prepareSize - local.set $3 - local.get $1 - i32.load - local.set $4 - local.get $4 - i32.const 3 - i32.const -1 - i32.xor - i32.and - local.set $5 - local.get $3 - local.get $5 - i32.le_u - if - local.get $0 - local.get $1 - local.get $3 - call $~lib/rt/tlsf/prepareBlock - i32.const 0 - drop - local.get $1 - return - end - local.get $1 - local.set $6 - local.get $6 - i32.const 4 - i32.add - local.get $6 - i32.load - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.set $7 - local.get $7 - i32.load - local.set $8 - local.get $8 - i32.const 1 - i32.and - if - local.get $5 - i32.const 4 - i32.add - local.get $8 - i32.const 3 - i32.const -1 - i32.xor - i32.and - i32.add - local.set $6 - local.get $6 - local.get $3 - i32.ge_u - if - local.get $0 - local.get $7 - call $~lib/rt/tlsf/removeBlock - local.get $1 - local.get $4 - i32.const 3 - i32.and - local.get $6 - i32.or - i32.store - local.get $0 - local.get $1 - local.get $3 - call $~lib/rt/tlsf/prepareBlock - i32.const 0 - drop - local.get $1 - return - end - end - local.get $0 - local.get $1 - local.get $2 - call $~lib/rt/tlsf/moveBlock - ) - (func $~lib/rt/tlsf/__realloc (param $0 i32) (param $1 i32) (result i32) - global.get $~lib/rt/tlsf/ROOT - i32.eqz - if - call $~lib/rt/tlsf/initialize - end - local.get $0 - global.get $~lib/memory/__heap_base - i32.lt_u - if (result i32) - global.get $~lib/rt/tlsf/ROOT - local.get $0 - call $~lib/rt/tlsf/checkUsedBlock - local.get $1 - call $~lib/rt/tlsf/moveBlock - else - global.get $~lib/rt/tlsf/ROOT - local.get $0 - call $~lib/rt/tlsf/checkUsedBlock - local.get $1 - call $~lib/rt/tlsf/reallocateBlock - end - i32.const 4 - i32.add - ) - (func $~lib/rt/pure/__renew (param $0 i32) (param $1 i32) (result i32) - (local $2 i32) - local.get $1 - i32.const 1073741804 - i32.gt_u - if - i32.const 32 - i32.const 96 - i32.const 288 - i32.const 30 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.const 16 - i32.sub - i32.const 16 - local.get $1 - i32.add - call $~lib/rt/tlsf/__realloc - local.set $2 - local.get $2 - i32.const 4 - i32.sub - local.get $1 - i32.store offset=16 - local.get $2 - i32.const 16 - i32.add - ) - (func $~lib/rt/pure/increment (param $0 i32) - (local $1 i32) - local.get $0 - i32.load offset=4 - local.set $1 - local.get $1 - i32.const 268435455 - i32.const -1 - i32.xor - i32.and - local.get $1 - i32.const 1 - i32.add - i32.const 268435455 - i32.const -1 - i32.xor - i32.and - i32.eq - i32.eqz - if - i32.const 0 - i32.const 96 - i32.const 109 - i32.const 3 - call $~lib/builtins/abort - unreachable - end - local.get $0 - local.get $1 - i32.const 1 - i32.add - i32.store offset=4 - i32.const 0 - drop - i32.const 1 - drop - local.get $0 - i32.load - i32.const 1 - i32.and - i32.eqz - i32.eqz - if - i32.const 0 - i32.const 96 - i32.const 112 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - ) - (func $~lib/rt/pure/__retain (param $0 i32) (result i32) - local.get $0 - global.get $~lib/memory/__heap_base - i32.gt_u - if - local.get $0 - i32.const 20 - i32.sub - call $~lib/rt/pure/increment - end - local.get $0 - ) - (func $~lib/rt/pure/__release (param $0 i32) - local.get $0 - global.get $~lib/memory/__heap_base - i32.gt_u - if - local.get $0 - i32.const 20 - i32.sub - call $~lib/rt/pure/decrement - end - ) - (func $~lib/memory/memory.fill (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i64) - (local $10 i32) - block $~lib/util/memory/memset|inlined.0 - local.get $0 - local.set $5 - local.get $1 - local.set $4 - local.get $2 - local.set $3 - i32.const 0 - i32.const 1 - i32.gt_s - drop - local.get $3 - i32.eqz - if - br $~lib/util/memory/memset|inlined.0 - end - local.get $5 - local.get $3 - i32.add - i32.const 4 - i32.sub - local.set $6 - local.get $5 - local.get $4 - i32.store8 - local.get $6 - local.get $4 - i32.store8 offset=3 - local.get $3 - i32.const 2 - i32.le_u - if - br $~lib/util/memory/memset|inlined.0 - end - local.get $5 - local.get $4 - i32.store8 offset=1 - local.get $5 - local.get $4 - i32.store8 offset=2 - local.get $6 - local.get $4 - i32.store8 offset=2 - local.get $6 - local.get $4 - i32.store8 offset=1 - local.get $3 - i32.const 6 - i32.le_u - if - br $~lib/util/memory/memset|inlined.0 - end - local.get $5 - local.get $4 - i32.store8 offset=3 - local.get $6 - local.get $4 - i32.store8 - local.get $3 - i32.const 8 - i32.le_u - if - br $~lib/util/memory/memset|inlined.0 - end - i32.const 0 - local.get $5 - i32.sub - i32.const 3 - i32.and - local.set $7 - local.get $5 - local.get $7 - i32.add - local.set $5 - local.get $3 - local.get $7 - i32.sub - local.set $3 - local.get $3 - i32.const -4 - i32.and - local.set $3 - i32.const -1 - i32.const 255 - i32.div_u - local.get $4 - i32.const 255 - i32.and - i32.mul - local.set $8 - local.get $5 - local.get $3 - i32.add - i32.const 28 - i32.sub - local.set $6 - local.get $5 - local.get $8 - i32.store - local.get $6 - local.get $8 - i32.store offset=24 - local.get $3 - i32.const 8 - i32.le_u - if - br $~lib/util/memory/memset|inlined.0 - end - local.get $5 - local.get $8 - i32.store offset=4 - local.get $5 - local.get $8 - i32.store offset=8 - local.get $6 - local.get $8 - i32.store offset=16 - local.get $6 - local.get $8 - i32.store offset=20 - local.get $3 - i32.const 24 - i32.le_u - if - br $~lib/util/memory/memset|inlined.0 - end - local.get $5 - local.get $8 - i32.store offset=12 - local.get $5 - local.get $8 - i32.store offset=16 - local.get $5 - local.get $8 - i32.store offset=20 - local.get $5 - local.get $8 - i32.store offset=24 - local.get $6 - local.get $8 - i32.store - local.get $6 - local.get $8 - i32.store offset=4 - local.get $6 - local.get $8 - i32.store offset=8 - local.get $6 - local.get $8 - i32.store offset=12 - i32.const 24 - local.get $5 - i32.const 4 - i32.and - i32.add - local.set $7 - local.get $5 - local.get $7 - i32.add - local.set $5 - local.get $3 - local.get $7 - i32.sub - local.set $3 - local.get $8 - i64.extend_i32_u - local.get $8 - i64.extend_i32_u - i64.const 32 - i64.shl - i64.or - local.set $9 - loop $while-continue|0 - local.get $3 - i32.const 32 - i32.ge_u - local.set $10 - local.get $10 - if - local.get $5 - local.get $9 - i64.store - local.get $5 - local.get $9 - i64.store offset=8 - local.get $5 - local.get $9 - i64.store offset=16 - local.get $5 - local.get $9 - i64.store offset=24 - local.get $3 - i32.const 32 - i32.sub - local.set $3 - local.get $5 - i32.const 32 - i32.add - local.set $5 - br $while-continue|0 - end - end - end - ) - (func $~lib/arraybuffer/ArrayBufferView#constructor (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - local.get $0 - i32.eqz - if - i32.const 12 - i32.const 2 - call $~lib/rt/pure/__new - call $~lib/rt/pure/__retain - local.set $0 - end - local.get $0 - i32.const 0 - i32.store - local.get $0 - i32.const 0 - i32.store offset=4 - local.get $0 - i32.const 0 - i32.store offset=8 - local.get $1 - i32.const 1073741820 - local.get $2 - i32.shr_u - i32.gt_u - if - i32.const 816 - i32.const 864 - i32.const 18 - i32.const 57 - call $~lib/builtins/abort - unreachable - end - local.get $1 - local.get $2 - i32.shl - local.tee $1 - i32.const 0 - call $~lib/rt/pure/__new - local.set $3 - local.get $3 - i32.const 0 - local.get $1 - call $~lib/memory/memory.fill - local.get $0 - local.tee $4 - local.get $3 - local.tee $5 - local.get $4 - i32.load - local.tee $6 - i32.ne - if - local.get $5 - call $~lib/rt/pure/__retain - local.set $5 - local.get $6 - call $~lib/rt/pure/__release - end - local.get $5 - i32.store - local.get $0 - local.get $3 - i32.store offset=4 - local.get $0 - local.get $1 - i32.store offset=8 - local.get $0 - ) - (func $~lib/typedarray/Float64Array#constructor (param $0 i32) (param $1 i32) (result i32) - local.get $0 - i32.eqz - if - i32.const 12 - i32.const 4 - call $~lib/rt/pure/__new - call $~lib/rt/pure/__retain - local.set $0 - end - local.get $0 - local.get $1 - i32.const 3 - call $~lib/arraybuffer/ArrayBufferView#constructor - local.set $0 - local.get $0 - ) - (func $~lib/array/Array#get:length (param $0 i32) (result i32) - local.get $0 - i32.load offset=12 - ) - (func $~lib/typedarray/Float64Array#get:length (param $0 i32) (result i32) - local.get $0 - i32.load offset=8 - i32.const 3 - i32.shr_u - ) - (func $~lib/typedarray/Float64Array#set<~lib/array/Array> (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - local.get $1 - call $~lib/rt/pure/__retain - local.set $1 - local.get $0 - call $~lib/rt/pure/__retain - local.set $5 - local.get $1 - call $~lib/rt/pure/__retain - local.set $4 - local.get $2 - local.set $3 - i32.const 0 - drop - local.get $3 - i32.const 0 - i32.lt_s - if - i32.const 928 - i32.const 992 - i32.const 1774 - i32.const 19 - call $~lib/builtins/abort - unreachable - end - local.get $4 - call $~lib/array/Array#get:length - local.get $3 - i32.add - local.get $5 - call $~lib/typedarray/Float64Array#get:length - i32.gt_s - if - i32.const 928 - i32.const 992 - i32.const 1775 - i32.const 47 - call $~lib/builtins/abort - unreachable - end - i32.const 0 - i32.const 0 - i32.eq - if (result i32) - i32.const 3 - i32.const 3 - i32.eq - else - i32.const 0 - end - if (result i32) - i32.const 0 - if (result i32) - i32.const 0 - else - i32.const 0 - end - i32.eqz - else - i32.const 0 - end - drop - local.get $5 - i32.load offset=4 - local.get $3 - i32.const 3 - i32.shl - i32.add - local.get $4 - i32.load offset=4 - local.get $4 - i32.load offset=8 - call $~lib/memory/memory.copy - local.get $4 - call $~lib/rt/pure/__release - local.get $5 - call $~lib/rt/pure/__release - local.get $1 - call $~lib/rt/pure/__release - ) - (func $~lib/typedarray/Float64Array#fill (param $0 i32) (param $1 f64) (param $2 i32) (param $3 i32) (result i32) - (local $4 i32) - (local $5 i32) - (local $6 f64) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i32) - (local $11 i32) - local.get $0 - call $~lib/rt/pure/__retain - local.set $7 - local.get $1 - local.set $6 - local.get $2 - local.set $5 - local.get $3 - local.set $4 - local.get $7 - i32.load offset=4 - local.set $8 - local.get $7 - call $~lib/typedarray/Float64Array#get:length - local.set $9 - local.get $5 - i32.const 0 - i32.lt_s - if (result i32) - local.get $9 - local.get $5 - i32.add - local.tee $10 - i32.const 0 - local.tee $11 - local.get $10 - local.get $11 - i32.gt_s - select - else - local.get $5 - local.tee $11 - local.get $9 - local.tee $10 - local.get $11 - local.get $10 - i32.lt_s - select - end - local.set $5 - local.get $4 - i32.const 0 - i32.lt_s - if (result i32) - local.get $9 - local.get $4 - i32.add - local.tee $10 - i32.const 0 - local.tee $11 - local.get $10 - local.get $11 - i32.gt_s - select - else - local.get $4 - local.tee $11 - local.get $9 - local.tee $10 - local.get $11 - local.get $10 - i32.lt_s - select - end - local.set $4 - i32.const 8 - i32.const 1 - i32.eq - drop - loop $for-loop|0 - local.get $5 - local.get $4 - i32.lt_s - local.set $11 - local.get $11 - if - local.get $8 - local.get $5 - i32.const 3 - i32.shl - i32.add - local.get $6 - f64.store - local.get $5 - i32.const 1 - i32.add - local.set $5 - br $for-loop|0 - end - end - local.get $7 - ) - (func $start:assembly/index - (local $0 i32) - (local $1 i32) - i32.const 0 - global.get $assembly/index/HLF_LEN - call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/pinkNoise - global.get $assembly/index/pinkNoise - global.get $assembly/index/pNoise - i32.const 0 - call $~lib/typedarray/Float64Array#set<~lib/array/Array> - i32.const 0 - global.get $assembly/index/DAT_LEN - call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/inputData - global.get $assembly/index/inputData - f64.const 0 - i32.const 0 - global.get $~lib/builtins/i32.MAX_VALUE - call $~lib/typedarray/Float64Array#fill - call $~lib/rt/pure/__release - i32.const 0 - global.get $assembly/index/DAT_LEN - call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/outputData - global.get $assembly/index/outputData - f64.const 0 - i32.const 0 - global.get $~lib/builtins/i32.MAX_VALUE - call $~lib/typedarray/Float64Array#fill - call $~lib/rt/pure/__release - i32.const 0 - i32.const 10 - call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/audioProps - global.get $assembly/index/audioProps - f64.const 0 - i32.const 0 - global.get $~lib/builtins/i32.MAX_VALUE - call $~lib/typedarray/Float64Array#fill - call $~lib/rt/pure/__release - i32.const 0 - i32.const 11 - call $~lib/typedarray/Float64Array#constructor - global.set $assembly/index/audioSettings - global.get $assembly/index/audioSettings - f64.const 0 - i32.const 0 - global.get $~lib/builtins/i32.MAX_VALUE - call $~lib/typedarray/Float64Array#fill - call $~lib/rt/pure/__release - ) - (func $assembly/index/allocF64Array (param $0 i32) (result i32) - i32.const 0 - local.get $0 - call $~lib/typedarray/Float64Array#constructor - ) - (func $~lib/typedarray/Uint32Array#constructor (param $0 i32) (param $1 i32) (result i32) - local.get $0 - i32.eqz - if - i32.const 12 - i32.const 6 - call $~lib/rt/pure/__new - call $~lib/rt/pure/__retain - local.set $0 - end - local.get $0 - local.get $1 - i32.const 2 - call $~lib/arraybuffer/ArrayBufferView#constructor - local.set $0 - local.get $0 - ) - (func $assembly/index/allocU32Array (param $0 i32) (result i32) - i32.const 0 - local.get $0 - call $~lib/typedarray/Uint32Array#constructor - ) - (func $~lib/typedarray/Float64Array#__get (param $0 i32) (param $1 i32) (result f64) - local.get $1 - local.get $0 - i32.load offset=8 - i32.const 3 - i32.shr_u - i32.ge_u - if - i32.const 928 - i32.const 992 - i32.const 1304 - i32.const 64 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.load offset=4 - local.get $1 - i32.const 3 - i32.shl - i32.add - f64.load - ) - (func $assembly/index/isOn (param $0 f64) (result i32) - local.get $0 - f64.const 0 - f64.gt - ) - (func $~lib/typedarray/Float64Array#__set (param $0 i32) (param $1 i32) (param $2 f64) - local.get $1 - local.get $0 - i32.load offset=8 - i32.const 3 - i32.shr_u - i32.ge_u - if - i32.const 928 - i32.const 992 - i32.const 1315 - i32.const 64 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.load offset=4 - local.get $1 - i32.const 3 - i32.shl - i32.add - local.get $2 - f64.store - ) - (func $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> (param $0 i32) (param $1 i32) (param $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - local.get $1 - call $~lib/rt/pure/__retain - local.set $1 - local.get $0 - call $~lib/rt/pure/__retain - local.set $5 - local.get $1 - call $~lib/rt/pure/__retain - local.set $4 - local.get $2 - local.set $3 - i32.const 0 - drop - local.get $3 - i32.const 0 - i32.lt_s - if - i32.const 928 - i32.const 992 - i32.const 1774 - i32.const 19 - call $~lib/builtins/abort - unreachable - end - local.get $4 - call $~lib/typedarray/Float64Array#get:length - local.get $3 - i32.add - local.get $5 - call $~lib/typedarray/Float64Array#get:length - i32.gt_s - if - i32.const 928 - i32.const 992 - i32.const 1775 - i32.const 47 - call $~lib/builtins/abort - unreachable - end - i32.const 0 - i32.const 0 - i32.eq - if (result i32) - i32.const 3 - i32.const 3 - i32.eq - else - i32.const 0 - end - if (result i32) - i32.const 0 - if (result i32) - i32.const 0 - else - i32.const 0 - end - i32.eqz - else - i32.const 0 - end - drop - local.get $5 - i32.load offset=4 - local.get $3 - i32.const 3 - i32.shl - i32.add - local.get $4 - i32.load offset=4 - local.get $4 - i32.load offset=8 - call $~lib/memory/memory.copy - local.get $4 - call $~lib/rt/pure/__release - local.get $5 - call $~lib/rt/pure/__release - local.get $1 - call $~lib/rt/pure/__release - ) - (func $assembly/index/correctPinkNoise (param $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - local.get $0 - call $~lib/rt/pure/__retain - local.set $0 - i32.const 0 - global.get $assembly/index/DAT_LEN - call $~lib/typedarray/Float64Array#constructor - local.set $1 - i32.const 0 - local.set $2 - loop $for-loop|0 - local.get $2 - global.get $assembly/index/HLF_LEN - i32.lt_s - local.set $3 - local.get $3 - if - local.get $1 - local.get $2 - local.get $0 - local.get $2 - call $~lib/typedarray/Float64Array#__get - global.get $assembly/index/pinkNoise - local.get $2 - call $~lib/typedarray/Float64Array#__get - f64.div - call $~lib/typedarray/Float64Array#__set - local.get $1 - global.get $assembly/index/HLF_LEN - local.get $2 - i32.add - local.get $0 - global.get $assembly/index/HLF_LEN - local.get $2 - i32.add - call $~lib/typedarray/Float64Array#__get - global.get $assembly/index/pinkNoise - local.get $2 - call $~lib/typedarray/Float64Array#__get - f64.div - call $~lib/typedarray/Float64Array#__set - local.get $2 - i32.const 1 - i32.add - local.set $2 - br $for-loop|0 - end - end - local.get $0 - local.get $1 - i32.const 0 - call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - call $~lib/rt/pure/__release - ) - (func $assembly/index/stereoToMono (param $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - local.get $0 - call $~lib/rt/pure/__retain - local.set $0 - i32.const 0 - global.get $assembly/index/DAT_LEN - call $~lib/typedarray/Float64Array#constructor - local.set $1 - i32.const 0 - local.set $2 - i32.const 0 - local.set $3 - loop $for-loop|0 - local.get $3 - global.get $assembly/index/HLF_LEN - i32.lt_s - local.set $4 - local.get $4 - if - local.get $1 - local.get $2 - local.tee $5 - i32.const 1 - i32.add - local.set $2 - local.get $5 - local.get $0 - local.get $3 - call $~lib/typedarray/Float64Array#__get - call $~lib/typedarray/Float64Array#__set - local.get $1 - local.get $2 - local.tee $5 - i32.const 1 - i32.add - local.set $2 - local.get $5 - local.get $0 - global.get $assembly/index/HLF_LEN - local.get $3 - i32.add - call $~lib/typedarray/Float64Array#__get - call $~lib/typedarray/Float64Array#__set - local.get $3 - i32.const 1 - i32.add - local.set $3 - br $for-loop|0 - end - end - local.get $0 - local.get $1 - i32.const 0 - call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - call $~lib/rt/pure/__release - ) - (func $assembly/index/invertAll (param $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 f64) - local.get $0 - call $~lib/rt/pure/__retain - local.set $0 - i32.const 0 - local.set $1 - loop $for-loop|0 - local.get $1 - global.get $assembly/index/HLF_LEN - i32.lt_s - local.set $2 - local.get $2 - if - local.get $0 - local.get $1 - call $~lib/typedarray/Float64Array#__get - local.set $3 - local.get $0 - local.get $1 - local.get $0 - global.get $assembly/index/DAT_LEN - i32.const 1 - i32.sub - local.get $1 - i32.sub - call $~lib/typedarray/Float64Array#__get - call $~lib/typedarray/Float64Array#__set - local.get $0 - global.get $assembly/index/DAT_LEN - i32.const 1 - i32.sub - local.get $1 - i32.sub - local.get $3 - call $~lib/typedarray/Float64Array#__set - local.get $1 - i32.const 1 - i32.add - local.set $1 - br $for-loop|0 - end - end - local.get $0 - call $~lib/rt/pure/__release - ) - (func $assembly/index/invertFirst (param $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 f64) - local.get $0 - call $~lib/rt/pure/__retain - local.set $0 - i32.const 0 - local.set $1 - loop $for-loop|0 - local.get $1 - global.get $assembly/index/QRT_LEN - i32.lt_s - local.set $2 - local.get $2 - if - local.get $0 - local.get $1 - call $~lib/typedarray/Float64Array#__get - local.set $3 - local.get $0 - local.get $1 - local.get $0 - global.get $assembly/index/HLF_LEN - i32.const 1 - i32.sub - local.get $1 - i32.sub - call $~lib/typedarray/Float64Array#__get - call $~lib/typedarray/Float64Array#__set - local.get $0 - global.get $assembly/index/HLF_LEN - i32.const 1 - i32.sub - local.get $1 - i32.sub - local.get $3 - call $~lib/typedarray/Float64Array#__set - local.get $1 - i32.const 1 - i32.add - local.set $1 - br $for-loop|0 - end - end - local.get $0 - call $~lib/rt/pure/__release - ) - (func $assembly/index/invertSecond (param $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 f64) - local.get $0 - call $~lib/rt/pure/__retain - local.set $0 - i32.const 0 - local.set $1 - loop $for-loop|0 - local.get $1 - global.get $assembly/index/QRT_LEN - i32.lt_s - local.set $2 - local.get $2 - if - local.get $0 - global.get $assembly/index/HLF_LEN - local.get $1 - i32.add - call $~lib/typedarray/Float64Array#__get - local.set $3 - local.get $0 - global.get $assembly/index/HLF_LEN - local.get $1 - i32.add - local.get $0 - global.get $assembly/index/DAT_LEN - i32.const 1 - i32.sub - local.get $1 - i32.sub - call $~lib/typedarray/Float64Array#__get - call $~lib/typedarray/Float64Array#__set - local.get $0 - global.get $assembly/index/DAT_LEN - i32.const 1 - i32.sub - local.get $1 - i32.sub - local.get $3 - call $~lib/typedarray/Float64Array#__set - local.get $1 - i32.const 1 - i32.add - local.set $1 - br $for-loop|0 - end - end - local.get $0 - call $~lib/rt/pure/__release - ) - (func $~lib/math/NativeMath.pow (param $0 f64) (param $1 f64) (result f64) - (local $2 f64) - (local $3 f64) - (local $4 i32) - (local $5 i64) - (local $6 i64) - (local $7 i64) - (local $8 i64) - (local $9 i64) - (local $10 f64) - (local $11 i64) - (local $12 i32) - (local $13 i64) - (local $14 i64) - (local $15 f64) - (local $16 f64) - (local $17 f64) - (local $18 f64) - (local $19 f64) - (local $20 f64) - (local $21 f64) - (local $22 f64) - (local $23 f64) - (local $24 f64) - (local $25 f64) - (local $26 f64) - (local $27 f64) - (local $28 f64) - (local $29 f64) - (local $30 f64) - (local $31 f64) - (local $32 f64) - (local $33 f64) - (local $34 f64) - (local $35 f64) - (local $36 f64) - (local $37 f64) - (local $38 f64) - (local $39 i32) - (local $40 i32) - (local $41 i32) - (local $42 i32) - (local $43 i64) - (local $44 i64) - local.get $1 - f64.abs - f64.const 2 - f64.le - if - local.get $1 - f64.const 2 - f64.eq - if - local.get $0 - local.get $0 - f64.mul - return - end - local.get $1 - f64.const 0.5 - f64.eq - if - local.get $0 - f64.sqrt - f64.abs - f64.const inf - local.get $0 - f64.const inf - f64.neg - f64.ne - select - return - end - local.get $1 - f64.const -1 - f64.eq - if - f64.const 1 - local.get $0 - f64.div - return - end - local.get $1 - f64.const 1 - f64.eq - if - local.get $0 - return - end - local.get $1 - f64.const 0 - f64.eq - if - f64.const 1 - return - end - end - i32.const 0 - i32.const 1 - i32.lt_s - drop - block $~lib/util/math/pow_lut|inlined.0 (result f64) - local.get $0 - local.set $3 - local.get $1 - local.set $2 - i32.const 0 - local.set $4 - local.get $3 - i64.reinterpret_f64 - local.set $5 - local.get $2 - i64.reinterpret_f64 - local.set $6 - local.get $5 - i64.const 52 - i64.shr_u - local.set $7 - local.get $6 - i64.const 52 - i64.shr_u - local.set $8 - local.get $7 - i64.const 1 - i64.sub - i64.const 2047 - i64.const 1 - i64.sub - i64.ge_u - if (result i32) - i32.const 1 - else - local.get $8 - i64.const 2047 - i64.and - i64.const 958 - i64.sub - i64.const 1086 - i64.const 958 - i64.sub - i64.ge_u - end - if - local.get $6 - local.set $9 - local.get $9 - i64.const 1 - i64.shl - i64.const 1 - i64.sub - i64.const -9007199254740992 - i64.const 1 - i64.sub - i64.ge_u - if - local.get $6 - i64.const 1 - i64.shl - i64.const 0 - i64.eq - if - f64.const 1 - br $~lib/util/math/pow_lut|inlined.0 - end - local.get $5 - i64.const 4607182418800017408 - i64.eq - if - f64.const nan:0x8000000000000 - br $~lib/util/math/pow_lut|inlined.0 - end - local.get $5 - i64.const 1 - i64.shl - i64.const -9007199254740992 - i64.gt_u - if (result i32) - i32.const 1 - else - local.get $6 - i64.const 1 - i64.shl - i64.const -9007199254740992 - i64.gt_u - end - if - local.get $3 - local.get $2 - f64.add - br $~lib/util/math/pow_lut|inlined.0 - end - local.get $5 - i64.const 1 - i64.shl - i64.const 9214364837600034816 - i64.eq - if - f64.const nan:0x8000000000000 - br $~lib/util/math/pow_lut|inlined.0 - end - local.get $5 - i64.const 1 - i64.shl - i64.const 9214364837600034816 - i64.lt_u - local.get $6 - i64.const 63 - i64.shr_u - i64.const 0 - i64.ne - i32.eqz - i32.eq - if - f64.const 0 - br $~lib/util/math/pow_lut|inlined.0 - end - local.get $2 - local.get $2 - f64.mul - br $~lib/util/math/pow_lut|inlined.0 - end - local.get $5 - local.set $9 - local.get $9 - i64.const 1 - i64.shl - i64.const 1 - i64.sub - i64.const -9007199254740992 - i64.const 1 - i64.sub - i64.ge_u - if - local.get $3 - local.get $3 - f64.mul - local.set $10 - local.get $5 - i64.const 63 - i64.shr_u - i32.wrap_i64 - if (result i32) - block $~lib/util/math/checkint|inlined.0 (result i32) - local.get $6 - local.set $9 - local.get $9 - i64.const 52 - i64.shr_u - i64.const 2047 - i64.and - local.set $11 - local.get $11 - i64.const 1023 - i64.lt_u - if - i32.const 0 - br $~lib/util/math/checkint|inlined.0 - end - local.get $11 - i64.const 1023 - i64.const 52 - i64.add - i64.gt_u - if - i32.const 2 - br $~lib/util/math/checkint|inlined.0 - end - i64.const 1 - i64.const 1023 - i64.const 52 - i64.add - local.get $11 - i64.sub - i64.shl - local.set $11 - local.get $9 - local.get $11 - i64.const 1 - i64.sub - i64.and - i64.const 0 - i64.ne - if - i32.const 0 - br $~lib/util/math/checkint|inlined.0 - end - local.get $9 - local.get $11 - i64.and - i64.const 0 - i64.ne - if - i32.const 1 - br $~lib/util/math/checkint|inlined.0 - end - i32.const 2 - end - i32.const 1 - i32.eq - else - i32.const 0 - end - if - local.get $10 - f64.neg - local.set $10 - end - local.get $6 - i64.const 63 - i64.shr_u - i64.const 0 - i64.ne - if (result f64) - f64.const 1 - local.get $10 - f64.div - else - local.get $10 - end - br $~lib/util/math/pow_lut|inlined.0 - end - local.get $5 - i64.const 63 - i64.shr_u - i64.const 0 - i64.ne - if - block $~lib/util/math/checkint|inlined.1 (result i32) - local.get $6 - local.set $9 - local.get $9 - i64.const 52 - i64.shr_u - i64.const 2047 - i64.and - local.set $11 - local.get $11 - i64.const 1023 - i64.lt_u - if - i32.const 0 - br $~lib/util/math/checkint|inlined.1 - end - local.get $11 - i64.const 1023 - i64.const 52 - i64.add - i64.gt_u - if - i32.const 2 - br $~lib/util/math/checkint|inlined.1 - end - i64.const 1 - i64.const 1023 - i64.const 52 - i64.add - local.get $11 - i64.sub - i64.shl - local.set $11 - local.get $9 - local.get $11 - i64.const 1 - i64.sub - i64.and - i64.const 0 - i64.ne - if - i32.const 0 - br $~lib/util/math/checkint|inlined.1 - end - local.get $9 - local.get $11 - i64.and - i64.const 0 - i64.ne - if - i32.const 1 - br $~lib/util/math/checkint|inlined.1 - end - i32.const 2 - end - local.set $12 - local.get $12 - i32.const 0 - i32.eq - if - local.get $3 - local.get $3 - f64.sub - local.get $3 - local.get $3 - f64.sub - f64.div - br $~lib/util/math/pow_lut|inlined.0 - end - local.get $12 - i32.const 1 - i32.eq - if - i32.const 262144 - local.set $4 - end - local.get $5 - i64.const 9223372036854775807 - i64.and - local.set $5 - local.get $7 - i64.const 2047 - i64.and - local.set $7 - end - local.get $8 - i64.const 2047 - i64.and - i64.const 958 - i64.sub - i64.const 1086 - i64.const 958 - i64.sub - i64.ge_u - if - local.get $5 - i64.const 4607182418800017408 - i64.eq - if - f64.const 1 - br $~lib/util/math/pow_lut|inlined.0 - end - local.get $8 - i64.const 2047 - i64.and - i64.const 958 - i64.lt_u - if - f64.const 1 - br $~lib/util/math/pow_lut|inlined.0 - end - local.get $5 - i64.const 4607182418800017408 - i64.gt_u - local.get $8 - i64.const 2048 - i64.lt_u - i32.eq - if (result f64) - f64.const inf - else - f64.const 0 - end - br $~lib/util/math/pow_lut|inlined.0 - end - local.get $7 - i64.const 0 - i64.eq - if - local.get $3 - f64.const 4503599627370496 - f64.mul - i64.reinterpret_f64 - local.set $5 - local.get $5 - i64.const 9223372036854775807 - i64.and - local.set $5 - local.get $5 - i64.const 52 - i64.const 52 - i64.shl - i64.sub - local.set $5 - end - end - local.get $5 - local.set $9 - local.get $9 - i64.const 4604531861337669632 - i64.sub - local.set $11 - local.get $11 - i64.const 52 - i64.const 7 - i64.sub - i64.shr_u - i64.const 127 - i64.and - i32.wrap_i64 - local.set $12 - local.get $11 - i64.const 52 - i64.shr_s - local.set $13 - local.get $9 - local.get $11 - i64.const 4095 - i64.const 52 - i64.shl - i64.and - i64.sub - local.set $14 - local.get $14 - f64.reinterpret_i64 - local.set $10 - local.get $13 - f64.convert_i64_s - local.set $15 - i32.const 1032 - local.get $12 - i32.const 2 - i32.const 3 - i32.add - i32.shl - i32.add - f64.load - local.set $16 - i32.const 1032 - local.get $12 - i32.const 2 - i32.const 3 - i32.add - i32.shl - i32.add - f64.load offset=16 - local.set $17 - i32.const 1032 - local.get $12 - i32.const 2 - i32.const 3 - i32.add - i32.shl - i32.add - f64.load offset=24 - local.set $18 - local.get $14 - i64.const 2147483648 - i64.add - i64.const -4294967296 - i64.and - f64.reinterpret_i64 - local.set $19 - local.get $10 - local.get $19 - f64.sub - local.set $20 - local.get $19 - local.get $16 - f64.mul - f64.const 1 - f64.sub - local.set $21 - local.get $20 - local.get $16 - f64.mul - local.set $22 - local.get $21 - local.get $22 - f64.add - local.set $23 - local.get $15 - f64.const 0.6931471805598903 - f64.mul - local.get $17 - f64.add - local.set $24 - local.get $24 - local.get $23 - f64.add - local.set $25 - local.get $15 - f64.const 5.497923018708371e-14 - f64.mul - local.get $18 - f64.add - local.set $26 - local.get $24 - local.get $25 - f64.sub - local.get $23 - f64.add - local.set $27 - f64.const -0.5 - local.get $23 - f64.mul - local.set $28 - local.get $23 - local.get $28 - f64.mul - local.set $29 - local.get $23 - local.get $29 - f64.mul - local.set $30 - f64.const -0.5 - local.get $21 - f64.mul - local.set $31 - local.get $21 - local.get $31 - f64.mul - local.set $32 - local.get $25 - local.get $32 - f64.add - local.set $33 - local.get $22 - local.get $28 - local.get $31 - f64.add - f64.mul - local.set $34 - local.get $25 - local.get $33 - f64.sub - local.get $32 - f64.add - local.set $35 - local.get $30 - f64.const -0.6666666666666679 - local.get $23 - f64.const 0.5000000000000007 - f64.mul - f64.add - local.get $29 - f64.const 0.7999999995323976 - local.get $23 - f64.const -0.6666666663487739 - f64.mul - f64.add - local.get $29 - f64.const -1.142909628459501 - local.get $23 - f64.const 1.0000415263675542 - f64.mul - f64.add - f64.mul - f64.add - f64.mul - f64.add - f64.mul - local.set $36 - local.get $26 - local.get $27 - f64.add - local.get $34 - f64.add - local.get $35 - f64.add - local.get $36 - f64.add - local.set $37 - local.get $33 - local.get $37 - f64.add - local.set $38 - local.get $33 - local.get $38 - f64.sub - local.get $37 - f64.add - global.set $~lib/util/math/log_tail - local.get $38 - local.set $38 - global.get $~lib/util/math/log_tail - local.set $37 - local.get $6 - i64.const -134217728 - i64.and - f64.reinterpret_i64 - local.set $34 - local.get $2 - local.get $34 - f64.sub - local.set $33 - local.get $38 - i64.reinterpret_f64 - i64.const -134217728 - i64.and - f64.reinterpret_i64 - local.set $32 - local.get $38 - local.get $32 - f64.sub - local.get $37 - f64.add - local.set $31 - local.get $34 - local.get $32 - f64.mul - local.set $36 - local.get $33 - local.get $32 - f64.mul - local.get $2 - local.get $31 - f64.mul - f64.add - local.set $35 - block $~lib/util/math/exp_inline|inlined.0 (result f64) - local.get $36 - local.set $15 - local.get $35 - local.set $10 - local.get $4 - local.set $12 - local.get $15 - i64.reinterpret_f64 - local.set $9 - local.get $9 - i64.const 52 - i64.shr_u - i32.wrap_i64 - i32.const 2047 - i32.and - local.set $39 - local.get $39 - i32.const 969 - i32.sub - i32.const 63 - i32.ge_u - if - local.get $39 - i32.const 969 - i32.sub - i32.const -2147483648 - i32.ge_u - if - f64.const -1 - f64.const 1 - local.get $12 - select - br $~lib/util/math/exp_inline|inlined.0 - end - local.get $39 - i32.const 1033 - i32.ge_u - if - local.get $9 - i64.const 63 - i64.shr_u - i64.const 0 - i64.ne - if (result f64) - local.get $12 - local.set $41 - local.get $41 - local.set $42 - i64.const 1152921504606846976 - f64.reinterpret_i64 - local.set $16 - local.get $16 - f64.neg - local.get $16 - local.get $42 - select - local.get $16 - f64.mul - else - local.get $12 - local.set $42 - local.get $42 - local.set $41 - i64.const 8070450532247928832 - f64.reinterpret_i64 - local.set $17 - local.get $17 - f64.neg - local.get $17 - local.get $41 - select - local.get $17 - f64.mul - end - br $~lib/util/math/exp_inline|inlined.0 - end - i32.const 0 - local.set $39 - end - f64.const 184.6649652337873 - local.get $15 - f64.mul - local.set $29 - local.get $29 - f64.const 6755399441055744 - f64.add - local.set $30 - local.get $30 - i64.reinterpret_f64 - local.set $14 - local.get $30 - f64.const 6755399441055744 - f64.sub - local.set $30 - local.get $15 - local.get $30 - f64.const -0.005415212348111709 - f64.mul - f64.add - local.get $30 - f64.const -1.2864023111638346e-14 - f64.mul - f64.add - local.set $28 - local.get $28 - local.get $10 - f64.add - local.set $28 - local.get $14 - i64.const 127 - i64.and - i64.const 1 - i64.shl - i32.wrap_i64 - local.set $40 - local.get $14 - local.get $12 - i64.extend_i32_u - i64.add - i64.const 52 - i64.const 7 - i64.sub - i64.shl - local.set $13 - i32.const 5128 - local.get $40 - i32.const 3 - i32.shl - i32.add - i64.load - f64.reinterpret_i64 - local.set $25 - i32.const 5128 - local.get $40 - i32.const 3 - i32.shl - i32.add - i64.load offset=8 - local.get $13 - i64.add - local.set $11 - local.get $28 - local.get $28 - f64.mul - local.set $27 - local.get $25 - local.get $28 - f64.add - local.get $27 - f64.const 0.49999999999996786 - local.get $28 - f64.const 0.16666666666665886 - f64.mul - f64.add - f64.mul - f64.add - local.get $27 - local.get $27 - f64.mul - f64.const 0.0416666808410674 - local.get $28 - f64.const 0.008333335853059549 - f64.mul - f64.add - f64.mul - f64.add - local.set $24 - local.get $39 - i32.const 0 - i32.eq - if - block $~lib/util/math/specialcase|inlined.0 (result f64) - local.get $24 - local.set $18 - local.get $11 - local.set $44 - local.get $14 - local.set $43 - local.get $43 - i64.const 2147483648 - i64.and - i64.const 0 - i64.ne - i32.eqz - if - local.get $44 - i64.const 1009 - i64.const 52 - i64.shl - i64.sub - local.set $44 - local.get $44 - f64.reinterpret_i64 - local.set $17 - f64.const 5486124068793688683255936e279 - local.get $17 - local.get $17 - local.get $18 - f64.mul - f64.add - f64.mul - br $~lib/util/math/specialcase|inlined.0 - end - local.get $44 - i64.const 1022 - i64.const 52 - i64.shl - i64.add - local.set $44 - local.get $44 - f64.reinterpret_i64 - local.set $17 - local.get $17 - local.get $17 - local.get $18 - f64.mul - f64.add - local.set $16 - local.get $16 - f64.abs - f64.const 1 - f64.lt - if - f64.const 1 - local.get $16 - f64.copysign - local.set $23 - local.get $17 - local.get $16 - f64.sub - local.get $17 - local.get $18 - f64.mul - f64.add - local.set $22 - local.get $23 - local.get $16 - f64.add - local.set $21 - local.get $23 - local.get $21 - f64.sub - local.get $16 - f64.add - local.get $22 - f64.add - local.set $22 - local.get $21 - local.get $22 - f64.add - local.get $23 - f64.sub - local.set $16 - local.get $16 - f64.const 0 - f64.eq - if - local.get $44 - i64.const -9223372036854775808 - i64.and - f64.reinterpret_i64 - local.set $16 - end - end - local.get $16 - f64.const 2.2250738585072014e-308 - f64.mul - end - br $~lib/util/math/exp_inline|inlined.0 - end - local.get $11 - f64.reinterpret_i64 - local.set $26 - local.get $26 - local.get $26 - local.get $24 - f64.mul - f64.add - end - end - return - ) - (func $assembly/index/peakFilter (param $0 i32) (param $1 f64) - (local $2 f64) - (local $3 f64) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 f64) - local.get $0 - call $~lib/rt/pure/__retain - local.set $0 - f64.const 0 - local.set $2 - f64.const 0 - local.set $3 - i32.const 0 - global.get $assembly/index/DAT_LEN - call $~lib/typedarray/Float64Array#constructor - local.set $4 - i32.const 0 - local.set $5 - loop $for-loop|0 - local.get $5 - global.get $assembly/index/DAT_LEN - i32.lt_s - local.set $6 - local.get $6 - if - local.get $0 - local.get $5 - call $~lib/typedarray/Float64Array#__get - local.get $2 - f64.gt - if - local.get $0 - local.get $5 - call $~lib/typedarray/Float64Array#__get - local.set $2 - end - local.get $4 - local.get $5 - local.get $0 - local.get $5 - call $~lib/typedarray/Float64Array#__get - local.get $1 - f64.mul - local.get $1 - call $~lib/math/NativeMath.pow - call $~lib/typedarray/Float64Array#__set - local.get $4 - local.get $5 - call $~lib/typedarray/Float64Array#__get - local.get $3 - f64.gt - if - local.get $4 - local.get $5 - call $~lib/typedarray/Float64Array#__get - local.set $3 - end - local.get $5 - i32.const 1 - i32.add - local.set $5 - br $for-loop|0 - end - end - local.get $3 - local.get $2 - f64.div - local.set $7 - i32.const 0 - local.set $5 - loop $for-loop|1 - local.get $5 - global.get $assembly/index/DAT_LEN - i32.lt_s - local.set $6 - local.get $6 - if - local.get $0 - local.get $5 - local.get $4 - local.get $5 - call $~lib/typedarray/Float64Array#__get - local.get $7 - f64.div - call $~lib/typedarray/Float64Array#__set - local.get $5 - i32.const 1 - i32.add - local.set $5 - br $for-loop|1 - end - end - local.get $0 - call $~lib/rt/pure/__release - local.get $4 - call $~lib/rt/pure/__release - ) - (func $assembly/index/smoothArray (param $0 i32) (param $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 f64) - (local $6 i32) - (local $7 i32) - (local $8 i32) - local.get $0 - call $~lib/rt/pure/__retain - local.set $0 - i32.const 0 - global.get $assembly/index/DAT_LEN - call $~lib/typedarray/Float64Array#constructor - local.set $2 - i32.const 0 - local.set $3 - loop $for-loop|0 - local.get $3 - global.get $assembly/index/DAT_LEN - i32.lt_s - local.set $4 - local.get $4 - if - f64.const 0 - local.set $5 - local.get $3 - local.get $1 - i32.sub - local.set $6 - loop $for-loop|1 - local.get $6 - local.get $3 - local.get $1 - i32.add - i32.le_s - local.set $7 - local.get $7 - if - local.get $6 - local.set $8 - local.get $8 - i32.const 0 - i32.lt_s - if - local.get $8 - global.get $assembly/index/DAT_LEN - i32.add - local.set $8 - end - local.get $5 - local.get $0 - local.get $8 - global.get $assembly/index/DAT_LEN - i32.rem_s - call $~lib/typedarray/Float64Array#__get - f64.add - local.set $5 - local.get $6 - i32.const 1 - i32.add - local.set $6 - br $for-loop|1 - end - end - local.get $2 - local.get $3 - local.get $5 - local.get $1 - i32.const 2 - i32.mul - i32.const 1 - i32.add - f64.convert_i32_s - f64.div - call $~lib/typedarray/Float64Array#__set - local.get $3 - i32.const 1 - i32.add - local.set $3 - br $for-loop|0 - end - end - local.get $0 - local.get $2 - i32.const 0 - call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> - local.get $0 - call $~lib/rt/pure/__release - local.get $2 - call $~lib/rt/pure/__release - ) - (func $assembly/index/applyValueLeveling (param $0 i32) (param $1 i32) (param $2 f64) (param $3 f64) - (local $4 i32) - (local $5 i32) - (local $6 f64) - (local $7 f64) - local.get $0 - call $~lib/rt/pure/__retain - local.set $0 - local.get $1 - call $~lib/rt/pure/__retain - local.set $1 - i32.const 0 - local.set $4 - loop $for-loop|0 - local.get $4 - global.get $assembly/index/DAT_LEN - i32.lt_s - local.set $5 - local.get $5 - if - local.get $0 - local.get $4 - call $~lib/typedarray/Float64Array#__get - local.get $1 - local.get $4 - call $~lib/typedarray/Float64Array#__get - f64.sub - local.set $6 - f64.const 100 - local.get $6 - f64.const 0 - f64.gt - if (result f64) - local.get $2 - else - local.get $3 - end - f64.sub - local.set $7 - local.get $0 - local.get $4 - local.get $0 - local.get $4 - call $~lib/typedarray/Float64Array#__get - local.get $6 - local.get $7 - f64.mul - f64.const 100 - f64.div - f64.sub - call $~lib/typedarray/Float64Array#__set - local.get $4 - i32.const 1 - i32.add - local.set $4 - br $for-loop|0 - end - end - local.get $0 - call $~lib/rt/pure/__release - local.get $1 - call $~lib/rt/pure/__release - ) - (func $~lib/rt/__newBuffer (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) - local.get $0 - local.get $1 - call $~lib/rt/pure/__new - local.set $3 - local.get $2 - if - local.get $3 - local.get $2 - local.get $0 - call $~lib/memory/memory.copy - end - local.get $3 - ) - (func $~lib/rt/__newArray (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (result i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - i32.const 16 - local.get $2 - call $~lib/rt/pure/__new - local.set $4 - local.get $0 - local.get $1 - i32.shl - local.set $5 - local.get $5 - i32.const 0 - local.get $3 - call $~lib/rt/__newBuffer - local.set $6 - local.get $4 - local.get $6 - call $~lib/rt/pure/__retain - i32.store - local.get $4 - local.get $6 - i32.store offset=4 - local.get $4 - local.get $5 - i32.store offset=8 - local.get $4 - local.get $0 - i32.store offset=12 - local.get $4 - ) - (func $assembly/index/update - (local $0 f64) - (local $1 f64) - (local $2 f64) - (local $3 f64) - (local $4 f64) - (local $5 f64) - (local $6 f64) - (local $7 i32) - (local $8 i32) - (local $9 f64) - (local $10 f64) - (local $11 f64) - (local $12 f64) - (local $13 f64) - (local $14 i32) - global.get $assembly/index/audioSettings - i32.const 0 - call $~lib/typedarray/Float64Array#__get - call $assembly/index/isOn - if - global.get $assembly/index/inputData - call $assembly/index/correctPinkNoise - end - global.get $assembly/index/audioSettings - i32.const 1 - call $~lib/typedarray/Float64Array#__get - call $assembly/index/isOn - if - global.get $assembly/index/inputData - call $assembly/index/stereoToMono - end - global.get $assembly/index/audioSettings - i32.const 2 - call $~lib/typedarray/Float64Array#__get - call $assembly/index/isOn - if - global.get $assembly/index/audioSettings - i32.const 1 - call $~lib/typedarray/Float64Array#__get - call $assembly/index/isOn - if - global.get $assembly/index/inputData - call $assembly/index/invertAll - else - global.get $assembly/index/inputData - call $assembly/index/invertFirst - end - else - global.get $assembly/index/audioSettings - i32.const 1 - call $~lib/typedarray/Float64Array#__get - call $assembly/index/isOn - if - global.get $assembly/index/inputData - call $assembly/index/invertSecond - end - end - global.get $assembly/index/audioSettings - i32.const 3 - call $~lib/typedarray/Float64Array#__get - call $assembly/index/isOn - if - global.get $assembly/index/inputData - global.get $assembly/index/audioSettings - i32.const 3 - call $~lib/typedarray/Float64Array#__get - f64.const 1 - f64.add - call $assembly/index/peakFilter - end - global.get $assembly/index/audioSettings - i32.const 4 - call $~lib/typedarray/Float64Array#__get - call $assembly/index/isOn - if - global.get $assembly/index/inputData - global.get $assembly/index/audioSettings - i32.const 4 - call $~lib/typedarray/Float64Array#__get - local.set $0 - local.get $0 - f64.floor - i32.trunc_f64_s - call $assembly/index/smoothArray - end - global.get $assembly/index/inputData - global.get $assembly/index/outputData - global.get $assembly/index/audioSettings - i32.const 5 - call $~lib/typedarray/Float64Array#__get - global.get $assembly/index/audioSettings - i32.const 6 - call $~lib/typedarray/Float64Array#__get - call $assembly/index/applyValueLeveling - f64.const 0 - local.set $1 - f64.const 1 - local.set $2 - f64.const 0 - local.set $3 - f64.const 0 - local.set $4 - f64.const 0 - local.set $5 - f64.const 0 - local.set $6 - i32.const 0 - local.set $7 - loop $for-loop|0 - local.get $7 - global.get $assembly/index/DAT_LEN - i32.lt_s - local.set $8 - local.get $8 - if - global.get $assembly/index/inputData - local.get $7 - call $~lib/typedarray/Float64Array#__get - local.set $9 - local.get $9 - local.get $2 - f64.lt - if - local.get $9 - local.set $2 - end - local.get $9 - local.get $3 - f64.gt - if - local.get $9 - local.set $3 - end - local.get $7 - i32.const 42 - i32.const 3 - i32.div_s - i32.lt_s - if - local.get $4 - local.get $9 - global.get $assembly/index/audioSettings - i32.const 9 - call $~lib/typedarray/Float64Array#__get - f64.mul - f64.add - local.set $4 - else - local.get $7 - i32.const 69 - i32.gt_s - if - local.get $6 - local.get $9 - global.get $assembly/index/audioSettings - i32.const 7 - call $~lib/typedarray/Float64Array#__get - f64.mul - f64.add - local.set $6 - else - local.get $5 - local.get $9 - global.get $assembly/index/audioSettings - i32.const 8 - call $~lib/typedarray/Float64Array#__get - f64.mul - f64.add - local.set $5 - end - end - local.get $1 - local.get $9 - f64.add - local.set $1 - local.get $7 - i32.const 1 - i32.add - local.set $7 - br $for-loop|0 - end - end - local.get $1 - global.get $assembly/index/DAT_LEN - f64.convert_i32_s - f64.div - local.set $10 - local.get $3 - global.get $assembly/index/audioSettings - i32.const 10 - call $~lib/typedarray/Float64Array#__get - f64.const 1e3 - f64.div - f64.lt - if (result f64) - f64.const 0.9999 - else - f64.const 0 - end - local.set $11 - local.get $4 - f64.const 8 - f64.mul - local.get $5 - f64.sub - local.get $6 - f64.add - f64.const 6 - f64.div - local.get $10 - f64.div - local.set $12 - local.get $3 - local.get $2 - f64.sub - local.set $13 - global.get $assembly/index/outputData - global.get $assembly/index/inputData - i32.const 0 - call $~lib/typedarray/Float64Array#set<~lib/typedarray/Float64Array> - global.get $assembly/index/audioProps - i32.const 10 - i32.const 3 - i32.const 3 - i32.const 0 - call $~lib/rt/__newArray - call $~lib/rt/pure/__retain - local.set $8 - local.get $8 - i32.load offset=4 - local.set $14 - local.get $14 - local.get $4 - f64.store - local.get $14 - local.get $5 - f64.store offset=8 - local.get $14 - local.get $6 - f64.store offset=16 - local.get $14 - local.get $1 - f64.store offset=24 - local.get $14 - local.get $2 - f64.store offset=32 - local.get $14 - local.get $3 - f64.store offset=40 - local.get $14 - local.get $10 - f64.store offset=48 - local.get $14 - local.get $13 - f64.store offset=56 - local.get $14 - local.get $11 - f64.store offset=64 - local.get $14 - local.get $12 - f64.store offset=72 - local.get $8 - local.tee $14 - i32.const 0 - call $~lib/typedarray/Float64Array#set<~lib/array/Array> - local.get $14 - call $~lib/rt/pure/__release - ) - (func $~start - call $start:assembly/index - ) - (func $~lib/rt/pure/finalize (param $0 i32) - i32.const 0 - drop - global.get $~lib/rt/tlsf/ROOT - local.get $0 - call $~lib/rt/tlsf/freeBlock - ) - (func $~lib/rt/pure/decrement (param $0 i32) - (local $1 i32) - (local $2 i32) - local.get $0 - i32.load offset=4 - local.set $1 - local.get $1 - i32.const 268435455 - i32.and - local.set $2 - i32.const 0 - drop - i32.const 1 - drop - local.get $0 - i32.load - i32.const 1 - i32.and - i32.eqz - i32.eqz - if - i32.const 0 - i32.const 96 - i32.const 122 - i32.const 14 - call $~lib/builtins/abort - unreachable - end - local.get $2 - i32.const 1 - i32.eq - if - local.get $0 - i32.const 20 - i32.add - i32.const 1 - call $~lib/rt/__visit_members - i32.const 1 - drop - i32.const 1 - drop - local.get $1 - i32.const -2147483648 - i32.and - i32.eqz - i32.eqz - if - i32.const 0 - i32.const 96 - i32.const 126 - i32.const 18 - call $~lib/builtins/abort - unreachable - end - local.get $0 - call $~lib/rt/pure/finalize - else - i32.const 1 - drop - local.get $2 - i32.const 0 - i32.gt_u - i32.eqz - if - i32.const 0 - i32.const 96 - i32.const 136 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - i32.const 1 - drop - local.get $0 - local.get $1 - i32.const 268435455 - i32.const -1 - i32.xor - i32.and - local.get $2 - i32.const 1 - i32.sub - i32.or - i32.store offset=4 - end - ) - (func $~lib/rt/pure/__visit (param $0 i32) (param $1 i32) - local.get $0 - global.get $~lib/memory/__heap_base - i32.lt_u - if - return - end - i32.const 1 - drop - i32.const 1 - drop - local.get $1 - i32.const 1 - i32.eq - i32.eqz - if - i32.const 0 - i32.const 96 - i32.const 69 - i32.const 16 - call $~lib/builtins/abort - unreachable - end - local.get $0 - i32.const 20 - i32.sub - call $~lib/rt/pure/decrement - ) - (func $~lib/arraybuffer/ArrayBuffer~visit (param $0 i32) (param $1 i32) - nop - ) - (func $~lib/string/String~visit (param $0 i32) (param $1 i32) - nop - ) - (func $~lib/arraybuffer/ArrayBufferView~visit (param $0 i32) (param $1 i32) - (local $2 i32) - local.get $0 - i32.load - local.tee $2 - if - local.get $2 - local.get $1 - call $~lib/rt/pure/__visit - end - ) - (func $~lib/array/Array#__visit (param $0 i32) (param $1 i32) - i32.const 0 - drop - local.get $0 - i32.load - local.get $1 - call $~lib/rt/pure/__visit - ) - (func $~lib/array/Array~visit (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - call $~lib/array/Array#__visit - ) - (func $~lib/typedarray/Float64Array~visit (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - call $~lib/arraybuffer/ArrayBufferView~visit - ) - (func $~lib/typedarray/Uint8ClampedArray~visit (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - call $~lib/arraybuffer/ArrayBufferView~visit - ) - (func $~lib/typedarray/Uint32Array~visit (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - call $~lib/arraybuffer/ArrayBufferView~visit - ) - (func $~lib/array/Array#__visit (param $0 i32) (param $1 i32) - i32.const 0 - drop - local.get $0 - i32.load - local.get $1 - call $~lib/rt/pure/__visit - ) - (func $~lib/array/Array~visit (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - call $~lib/array/Array#__visit - ) - (func $~lib/rt/__visit_members (param $0 i32) (param $1 i32) - block $invalid - block $~lib/array/Array - block $~lib/typedarray/Uint32Array - block $~lib/typedarray/Uint8ClampedArray - block $~lib/typedarray/Float64Array - block $~lib/array/Array - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $~lib/array/Array $~lib/typedarray/Float64Array $~lib/typedarray/Uint8ClampedArray $~lib/typedarray/Uint32Array $~lib/array/Array $invalid - end - local.get $0 - local.get $1 - call $~lib/arraybuffer/ArrayBuffer~visit - return - end - local.get $0 - local.get $1 - call $~lib/string/String~visit - return - end - local.get $0 - local.get $1 - call $~lib/arraybuffer/ArrayBufferView~visit - return - end - local.get $0 - local.get $1 - call $~lib/array/Array~visit - return - end - local.get $0 - local.get $1 - call $~lib/typedarray/Float64Array~visit - return - end - local.get $0 - local.get $1 - call $~lib/typedarray/Uint8ClampedArray~visit - return - end - local.get $0 - local.get $1 - call $~lib/typedarray/Uint32Array~visit - return - end - local.get $0 - local.get $1 - call $~lib/array/Array~visit - return - end - unreachable - ) -) diff --git a/src/wasc-builder/package.json b/src/wasc-builder/package.json index d44b5f3..fa5a299 100644 --- a/src/wasc-builder/package.json +++ b/src/wasc-builder/package.json @@ -1,10 +1,5 @@ { - "scripts": { - "asbuild:untouched": "asc assembly/index.ts --runtime full --target debug", - "asbuild:optimized": "asc assembly/index.ts --runtime full --target release", - "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized", - "test": "node tests" - }, + "scripts": {}, "dependencies": { "@assemblyscript/loader": "^0.17.7" }, diff --git a/src/wasc-builder/tests/index.js b/src/wasc-builder/tests/index.js deleted file mode 100644 index df97f5c..0000000 --- a/src/wasc-builder/tests/index.js +++ /dev/null @@ -1,4 +0,0 @@ -const assert = require("assert"); -const myModule = require(".."); -assert.equal(myModule.add(1, 2), 3); -console.log("ok"); diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 21c029f..33765e7 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -102,7 +102,7 @@ export class WEAS extends CComponent { constructor() { super(); // delay audio initialization - Ready.On(() => this.realInit()); + Ready().then(() => this.realInit()); } private async realInit() { diff --git a/src/worker-loader.d.ts b/src/worker-loader.d.ts index c67af6b..b61d94d 100644 --- a/src/worker-loader.d.ts +++ b/src/worker-loader.d.ts @@ -1,3 +1,7 @@ +/* + * This is a definition wrapper, connecting the "compiled" workers to the actual worker loader. + */ + declare module 'worker-loader!*' { // You need to change `Worker`, if you specified a different value for the `workerType` option class WebpackWorker extends Worker { From d2d2462d49d8a10e6f67f1220f65501834de755a Mon Sep 17 00:00:00 2001 From: hexxone Date: Sun, 10 Jan 2021 19:46:49 +0100 Subject: [PATCH 20/76] Move WASM Builder to Submodule --- README.md | 33 +++++ src/wasc-builder/.gitignore | 3 - src/wasc-builder/WascBuilderPlugin.js | 166 -------------------------- src/wasc-builder/package-lock.json | 33 ----- src/wasc-builder/package.json | 9 -- src/wasc-worker | 2 +- 6 files changed, 34 insertions(+), 212 deletions(-) create mode 100644 README.md delete mode 100644 src/wasc-builder/.gitignore delete mode 100644 src/wasc-builder/WascBuilderPlugin.js delete mode 100644 src/wasc-builder/package-lock.json delete mode 100644 src/wasc-builder/package.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..adcdcae --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +## we_utils + +### is a collection of utilities, mostly usefull when creating Wallpaper Engine Web Wallpapers with TypeScript / Webpack. + +I created this repository since I was previously copying back-and-forth lots of code between projects. +Keeping track of this stuff manually is annoying... + + +### Dependencies / Libraries +- [typescript](https://www.typescriptlang.org/) for typization +- [jQuery](https://jquery.com/) gui editing +- [three.js](https://threejs.org/) & Examples for webgl rendering + + +### Features / Contents +- OfflineWorker +- AssemblyScript Webpack Builder +- AssemblyScript WebAssembly Module Loader +- CComponent & CSettings Helpers +- Wallpaper Engine Audio Supplier (WEAS) +- "document.ready" shorthand +- ReloadHelper for displaying a loading bar +- Smallog Logging & Filtering +- Stats.js definition file +- WarnHelper for Seizure warnings +- Wallpaper Engine iCUE Library (WEICUE) +- Wallpaper Engine Wallpaper Adapter (WEWWA) +- Worker-Loader definition file + + +### Used by +- [AudiOrbits](https://github.com/hexxone/audiorbits) +- [ReactiveInk](https://github.com/hexxone/ReactiveInk) diff --git a/src/wasc-builder/.gitignore b/src/wasc-builder/.gitignore deleted file mode 100644 index ef24d8a..0000000 --- a/src/wasc-builder/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.wat - -./build/* \ No newline at end of file diff --git a/src/wasc-builder/WascBuilderPlugin.js b/src/wasc-builder/WascBuilderPlugin.js deleted file mode 100644 index be1481e..0000000 --- a/src/wasc-builder/WascBuilderPlugin.js +++ /dev/null @@ -1,166 +0,0 @@ -/** - * - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * This is a webpack plugin - * - */ - -const fs = require("fs"); -const path = require('path'); - -const asc = require("assemblyscript/bin/asc"); -const validate = require('schema-utils'); -const { RawSource } = require('webpack-sources'); - -const pluginName = 'WasmPlugin'; -const outPath = path.resolve(__dirname, 'build'); - -// schema for options object -const schema = { - type: 'object', - properties: { - production: { - type: 'boolean' - }, - relpath: { - type: 'string' - }, - regexx: { - type: 'object' - }, - cleanup: { - type: 'boolean' - } - } -}; - -// list files recursively -function getAllFiles(baseDir, subDir, arrayOfFiles) { - var sub = baseDir + "/" + subDir; - var files = fs.readdirSync(sub); - var arrayOfFiles = arrayOfFiles || []; - files.forEach((file) => { - var fle = subDir + "/" + file; - if (fs.statSync(sub + "/" + file).isDirectory()) - arrayOfFiles = getAllFiles(baseDir, fle, arrayOfFiles); - else - arrayOfFiles.push(fle); - }); - return arrayOfFiles; -} - -// compile assemblyscript (typescript) module to wasm and return binary -function compileWasm(inputPath, newName, production) { - return new Promise(resolve => { - try { - const newOut = path.resolve(outPath, newName); - - asc.main([ - inputPath, - "--extension", "asc", - "--binaryFile", newOut, - "--measure", - "--runtime", "full", - production ? "--optimize" : "--sourceMap" - ], (err) => { - //let output = execSync('npm run asbuild', { cwd: __dirname }); - if (err) throw err; - // none? -> read and resolve optimized.wasm string - resolve(production ? { - normal: fs.readFileSync(newOut) - } : { - normal: fs.readFileSync(newOut), - map: fs.readFileSync(newOut + ".map") - }); - }); - } - catch (ex) { - console.warn("[" + pluginName + "] Compile Error!"); - console.error(ex); - } - }); -} - -// delete all files in the output dir -function CleanUp() { - console.info("[" + pluginName + "] Cleaning..."); - return new Promise(resolve => { - fs.readdir(outPath, (err, files) => { - if (err) throw err; - Promise.all(files.map(file => { - return new Promise(res => { - fs.unlink(path.join(outPath, file), err => { - if (err) throw err; - console.info("[" + pluginName + "] delete: "+ file); - res(); - }); - }) - })).then(resolve); - }); - }); -} - -// actual webpack plugin -class WascBuilderPlugin { - - options = {}; - - constructor(options = {}) { - validate.validate(schema, options); - this.options = options; - } - - // Define `apply` as its prototype method which is supplied with compiler as its argument - apply(compiler) { - var addedOnce = false; - // Specify the event hook to attach to - compiler.hooks.thisCompilation.tap(pluginName, - (compilation) => compilation.hooks.processAssets.tap({ - name: pluginName, - stage: -2000, - }, async () => { - if (addedOnce) return; - addedOnce = true; - console.log("[" + pluginName + "] Gathering Infos...."); - - // add static files from folder - const rPath = path.resolve(__dirname, this.options.relpath); - var sFiles = getAllFiles(rPath, ""); - - for (var staticFile in sFiles) { - - const sFile = sFiles[staticFile]; - const sName = sFile.replace(/^.*[\\\/]/, ''); - - // if regex match wasm name, compile - if (sName.match(this.options.regexx)) { - console.info(`[${pluginName}] Compile ${this.options.production ? "prod" : "debug"} wasm: ${sName}`); - // keep ".wasm" and remove ".ts" part of name - const newName = sName.replace(/\.[^/.]+$/, ""); - - await compileWasm(rPath + sFile, newName, this.options.production).then(({ normal, map }) => { - console.info("[" + pluginName + "] Success: " + newName); - // emit files into compilation - if (normal) compilation.emitAsset(newName, new RawSource(normal)); - if (map) compilation.emitAsset(newName + ".map", new RawSource(map)); - }); - } - } - - // finalize - if(this.options.cleanup) await CleanUp(); - - console.info("[" + pluginName + "] finished."); - }) - ); - } -} - -module.exports = WascBuilderPlugin; \ No newline at end of file diff --git a/src/wasc-builder/package-lock.json b/src/wasc-builder/package-lock.json deleted file mode 100644 index 62991b7..0000000 --- a/src/wasc-builder/package-lock.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@assemblyscript/loader": { - "version": "0.17.7", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.17.7.tgz", - "integrity": "sha512-b3EESdpAEA7XqrnCBmkiM7bGX8r8M++6igAjy1LrflK4L3H78tCpXlKaams7/OEbwaycmyZL05vf7Yo8xCWBWA==" - }, - "assemblyscript": { - "version": "0.17.7", - "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.17.7.tgz", - "integrity": "sha512-HyFy/9FAXO/Q1HclibD6OsTleMfHJd3+gQRQtjLd9CbFmNeINxaxFHYlmzES2nqeSk9Jqxs29OOQaq13yboG4A==", - "dev": true, - "requires": { - "binaryen": "98.0.0-nightly.20201109", - "long": "^4.0.0" - } - }, - "binaryen": { - "version": "98.0.0-nightly.20201109", - "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-98.0.0-nightly.20201109.tgz", - "integrity": "sha512-iRarAqdH5lMWlMBzrDuJgLYJR2g4QXk93iYE2zpr6gEZkb/jCgDpPUXdhuN11Ge1zZ/6By4DwA1mmifcx7FWaw==", - "dev": true - }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "dev": true - } - } -} diff --git a/src/wasc-builder/package.json b/src/wasc-builder/package.json deleted file mode 100644 index fa5a299..0000000 --- a/src/wasc-builder/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "scripts": {}, - "dependencies": { - "@assemblyscript/loader": "^0.17.7" - }, - "devDependencies": { - "assemblyscript": "^0.17.7" - } -} diff --git a/src/wasc-worker b/src/wasc-worker index 4e21ab2..c9c73c2 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 4e21ab21c8a26cfabc0b166f16d51461258305f0 +Subproject commit c9c73c2421b264e617ac8b6129c8badbdd40f233 From d2d61c2abe6e19795e1fd5b64b2ffeadb2648bbc Mon Sep 17 00:00:00 2001 From: hexxone Date: Sun, 10 Jan 2021 19:51:46 +0100 Subject: [PATCH 21/76] Refactor --- src/CComponent.ts | 8 ++++---- src/Stats.ts | 10 +++++----- src/WEICUE.ts | 2 +- src/WEWWA.ts | 10 +++++----- src/offline/OfflinePlugin.js | 2 +- src/offline/OfflineWorker.ts | 2 +- src/wasc-worker | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/CComponent.ts b/src/CComponent.ts index 99e1ea7..e659daa 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -8,13 +8,13 @@ import { CSettings } from "./CSettings"; - export class CComponent { +export class CComponent { public settings: CSettings = null; // Important: Append your child objects, for settings to be applied correctly! public children: CComponent[] = []; - + public GetComponents() { var list: CComponent[] = [this]; this.children.forEach((ch) => list.push(...ch.GetComponents())); @@ -31,9 +31,9 @@ import { CSettings } from "./CSettings"; public GetSettingsObj() { let result = {} Object.getOwnPropertyNames(this.settings).forEach(p => { - if(typeof this.settings[p] == "function") return; + if (typeof this.settings[p] == "function") return; result[p] = this.settings[p]; }); return result; } - } \ No newline at end of file +} \ No newline at end of file diff --git a/src/Stats.ts b/src/Stats.ts index cf4554d..80d06cd 100644 --- a/src/Stats.ts +++ b/src/Stats.ts @@ -8,13 +8,13 @@ declare interface Stats { REVISION: number; dom: HTMLDivElement; - addPanel( panel: Stats.Panel ): Stats.Panel; - showPanel( id: number ): void; + addPanel(panel: Stats.Panel): Stats.Panel; + showPanel(id: number): void; begin(): void; end(): void; update(): void; domElement: HTMLDivElement; - setMode( id: number ): void; + setMode(id: number): void; } declare function Stats(): Stats; @@ -22,10 +22,10 @@ declare function Stats(): Stats; declare namespace Stats { interface Panel { dom: HTMLCanvasElement; - update( value: number, maxValue: number ): void; + update(value: number, maxValue: number): void; } - function Panel( name?: string, fg?: string, bg?: string ): Panel; + function Panel(name?: string, fg?: string, bg?: string): Panel; } export default Stats; \ No newline at end of file diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 0c8c9e6..d71ec68 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -55,7 +55,7 @@ export class WEICUE extends CComponent { public settings: CUESettings = new CUESettings(); public isAvailable: boolean = false; public PAUSED: boolean = false; - + constructor(weas: WEAS) { super(); this.weas = weas; diff --git a/src/WEWWA.ts b/src/WEWWA.ts index 1fa61e1..518d8f3 100644 --- a/src/WEWWA.ts +++ b/src/WEWWA.ts @@ -80,7 +80,7 @@ export class WEWWA { // intialize when ready Ready().then(() => { - if(CC) { /* This tells the compiler to include CookieConsent at this point. */ } + if (CC) { /* This tells the compiler to include CookieConsent at this point. */ } const cc = window['cookieconsent'].initialise({ palette: { popup: { background: "#000" }, @@ -128,7 +128,7 @@ export class WEWWA { var last = localStorage.getItem("wewwaLastProps"); if (last != null) { var merged = Object.assign(props, JSON.parse(last)); - merged.audioprocessing = { + merged.audioprocessing = { value: this.project.general.supportsaudioprocessing, type: "hidden" }; @@ -432,7 +432,7 @@ export class WEWWA { reset.classList.add("red") reset.innerHTML = "Reset Settings"; reset.addEventListener("click", e => { - if(!window.confirm("This action will clear ALL local data!\r\n\r\nAre you sure?")) return; + if (!window.confirm("This action will clear ALL local data!\r\n\r\nAre you sure?")) return; OfflineHelper.Reset().then(() => { localStorage.clear(); location = location; @@ -464,7 +464,7 @@ export class WEWWA { } private CreateItem(prop, itm) { - if(!itm.type || itm.type == "hidden") return; + if (!itm.type || itm.type == "hidden") return; var self = this; var ce = (e) => document.createElement(e); var row = ce("tr"); @@ -680,7 +680,7 @@ export class WEWWA { // get input dom element var elm: any = document.getElementById("wewwa_" + p); - if(!elm || elm.childNodes.length < 2) continue; + if (!elm || elm.childNodes.length < 2) continue; elm = elm.childNodes[1].childNodes[0]; switch (prop.type) { diff --git a/src/offline/OfflinePlugin.js b/src/offline/OfflinePlugin.js index 110f604..fc82e0d 100644 --- a/src/offline/OfflinePlugin.js +++ b/src/offline/OfflinePlugin.js @@ -98,7 +98,7 @@ class OfflinePlugin { // Loop through all compiled assets, // adding a new line item for each filename. for (var filename in compilation.assets) { - filelist.push('/'+ filename); + filelist.push('/' + filename); } // add additional files anyway? diff --git a/src/offline/OfflineWorker.ts b/src/offline/OfflineWorker.ts index 451cc8a..71eaf62 100644 --- a/src/offline/OfflineWorker.ts +++ b/src/offline/OfflineWorker.ts @@ -59,7 +59,7 @@ wrk.addEventListener("install", function (event: any) { // CSS resources, fonts, any images, etc. wrk.addEventListener("fetch", function (event: any) { //console.info(wName + 'fetch event in progress.'); - + if (event.request.method !== 'GET') { console.info(wName + 'fetch event ignored.', event.request.method, event.request.url); return; diff --git a/src/wasc-worker b/src/wasc-worker index c9c73c2..9f4f273 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit c9c73c2421b264e617ac8b6129c8badbdd40f233 +Subproject commit 9f4f27384591dc8a30ba407d201b071c964679eb From 02808036fb9d16636e645b39a2b64cf6209f78ea Mon Sep 17 00:00:00 2001 From: hexxone Date: Wed, 13 Jan 2021 23:20:55 +0100 Subject: [PATCH 22/76] pretty printing & submodule update --- src/offline/OfflinePlugin.js | 5 ++++- src/wasc-worker | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/offline/OfflinePlugin.js b/src/offline/OfflinePlugin.js index fc82e0d..f68da05 100644 --- a/src/offline/OfflinePlugin.js +++ b/src/offline/OfflinePlugin.js @@ -43,6 +43,9 @@ const schema = { }, extrafiles: { type: 'array' + }, + pretty: { + type: 'boolean' } } }; @@ -106,7 +109,7 @@ class OfflinePlugin { this.options.extrafiles.map(ef => filelist.push(ef)); // create the target file with all app-contents as json list - const jList = JSON.stringify(filelist, null, 1); + const jList = JSON.stringify(filelist, null, this.options.pretty ? 1 : 0); compilation.emitAsset(this.options.outfile, new RawSource(jList)); console.info("[OfflinePlugin] successfull: " + jList); diff --git a/src/wasc-worker b/src/wasc-worker index 9f4f273..dccd473 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 9f4f27384591dc8a30ba407d201b071c964679eb +Subproject commit dccd4730ccdbb935181308d9462c24ce82ba09db From 89e0028c9bb11d05523febfb52c1af12f62d4fa1 Mon Sep 17 00:00:00 2001 From: hexxone Date: Thu, 28 Jan 2021 05:56:08 +0100 Subject: [PATCH 23/76] Improving plugins & settings --- src/CComponent.ts | 5 ++++ src/ReloadHelper.ts | 21 +++++++++++++--- src/Stats.ts | 5 +++- src/WEICUE.ts | 1 + src/WEWWA.ts | 7 +++++- src/WarnHelper.ts | 48 +++++++++++++++++++++++------------- src/offline/OfflinePlugin.js | 8 +++--- src/wasc-worker | 2 +- src/weas/WEAS.ts | 15 +++-------- 9 files changed, 75 insertions(+), 37 deletions(-) diff --git a/src/CComponent.ts b/src/CComponent.ts index e659daa..10f60eb 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -1,6 +1,11 @@ /** * @author D.Thiele @https://hexx.one * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * * @description * represents a Core Component for Wallpaper Engine Wallpaper * diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index c3c0be3..b52cb9c 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -1,12 +1,26 @@ /** * @author D.Thiele @https://hexx.one + * + * @license + * Copyright (c) 2020 D.Thiele All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Displays a html reload bar for a given Time. + * */ +import { CSettings } from "./CSettings"; import { Ready } from "./Ready"; +class ReloadSettings extends CSettings { + reload_seconds: number = 3; +} + export class ReloadHelper { - waitSeconds = 3; + public settings: ReloadSettings = new ReloadSettings(); constructor() { Ready().then(() => { @@ -25,7 +39,7 @@ export class ReloadHelper { height: 10px; width: 0%; background-color: #989a; - transition: all ` + this.waitSeconds + `s ease, opacity 0.33s ease; + transition: all ${this.settings.reload_seconds}s ease, opacity 0.33s ease; } #reload-bar.show { opacity: 1; @@ -43,7 +57,7 @@ export class ReloadHelper { font-weight: 100; font-size: 3em; color: #fffa; - transition: all .33s ease, color ` + this.waitSeconds + `s ease, text-shadow ` + this.waitSeconds + `s ease; + transition: all .33s ease, color ${this.settings.reload_seconds}s ease, text-shadow ${this.settings.reload_seconds}s ease; } #reload-text.show { top: 10px; @@ -72,6 +86,7 @@ export class ReloadHelper { document.body.append(outer); } + // @Todo make bar always reset to 0 on show public Show() { $("#reload-bar, #reload-text").removeClass("done").addClass("show"); } diff --git a/src/Stats.ts b/src/Stats.ts index 80d06cd..fe1db4f 100644 --- a/src/Stats.ts +++ b/src/Stats.ts @@ -1,7 +1,10 @@ /** * @author D.Thiele @https://hexx.one * - * @description TypeScript Wrapper for mrdoob Stats.js + * @description + * TypeScript Wrapper for mrdoob Stats.js + * Still requires Stats.js to be included! + * */ diff --git a/src/WEICUE.ts b/src/WEICUE.ts index d71ec68..4498ff0 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -53,6 +53,7 @@ export class WEICUE extends CComponent { // runtime values public settings: CUESettings = new CUESettings(); + public isAvailable: boolean = false; public PAUSED: boolean = false; diff --git a/src/WEWWA.ts b/src/WEWWA.ts index 518d8f3..aa1739e 100644 --- a/src/WEWWA.ts +++ b/src/WEWWA.ts @@ -33,11 +33,16 @@ * - apply all settings once * - react to changes made in the ui and update them in the wallpaper * - save changes made in the ui to localStorage + * - Annoying Cookie Popup (Thanks DSGVO) * * @todo * - inject "audio processing" setting - * - Annoying Cookie Popup (Thanks DSGVO) * + * lighthouse: + * - image explicit width/height + * - cf longer cache policy (2d?) + * - { this.injectCSS(); this.injectHTML(); @@ -32,14 +45,15 @@ export class WarnHelper { private injectCSS() { var st = document.createElement("style"); st.innerHTML = ` - #triggerwarn { + #${ELM_ID} { object-fit: contain; + text-align: center; max-height: 30vmax; top: 25vmin; opacity: 0; - transition: opacity ` + this.animationSeconds + `s ease; + transition: opacity ${this.settings.animate_seconds}s ease; } - #triggerwarn.show { + #${ELM_ID}.show { opacity: 1; } `; @@ -47,24 +61,24 @@ export class WarnHelper { } private injectHTML() { - var outer = document.createElement("div"); - outer.id = "triggerwarn"; - outer.innerHTML = ` - - `; - document.body.append(outer); + this.element = document.createElement("div"); + this.element.id = ELM_ID; + document.body.append(this.element); } public Show() { return new Promise(resolve => { + // make text + const txtt = this.settings.seizure_text.replace("\r\n", "
"); + this.element.innerHTML = txtt; // show it - $("#triggerwarn").addClass("show"); + this.element.classList.add("show"); // wait some time setTimeout(() => { // hide it & wait again - $("#triggerwarn").removeClass("show"); - setTimeout(resolve, this.animationSeconds * 1000); - }, this.waitSeconds * 1000); + this.element.classList.remove("show"); + setTimeout(resolve, this.settings.animate_seconds * 1000); + }, this.settings.wait_seconds * 1000); }); } }; \ No newline at end of file diff --git a/src/offline/OfflinePlugin.js b/src/offline/OfflinePlugin.js index f68da05..d0365f7 100644 --- a/src/offline/OfflinePlugin.js +++ b/src/offline/OfflinePlugin.js @@ -27,6 +27,7 @@ const fs = require("fs"); const validate = require('schema-utils'); +const { Compilation } = require("webpack"); const { RawSource } = require('webpack-sources'); const pluginName = 'OfflinePlugin'; @@ -82,12 +83,12 @@ class OfflinePlugin { compiler.hooks.thisCompilation.tap(pluginName, (compilation) => compilation.hooks.processAssets.tap({ name: pluginName, - stage: -2000, + stage: Compilation.PROCESS_ASSETS_STAGE_ANALYSE, }, () => { if (addedOnce) return; addedOnce = true; - console.log('This is an experimental plugin!'); + console.info("[" + pluginName + "] Gathering Infos..."); // list of all app-contents var filelist = []; @@ -111,8 +112,9 @@ class OfflinePlugin { // create the target file with all app-contents as json list const jList = JSON.stringify(filelist, null, this.options.pretty ? 1 : 0); compilation.emitAsset(this.options.outfile, new RawSource(jList)); + console.info("[" + pluginName + "] result: " + jList); - console.info("[OfflinePlugin] successfull: " + jList); + console.info("[" + pluginName + "] finished."); } )); } diff --git a/src/wasc-worker b/src/wasc-worker index dccd473..cd4efae 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit dccd4730ccdbb935181308d9462c24ce82ba09db +Subproject commit cd4efae23b8855034dabd7470718340818d9c03b diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 33765e7..522163b 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -8,21 +8,14 @@ * * @description * WEWWA - * Wallpaper Engine Audio Supplier + * Wallpaper Engine Audio Supplier makes working with audio easier. + * It will automatically start to receive and process the audio data + * which can then be accessed on the global object. * * DEPENDS ON: - * - "./worker/weasWorker.js" * - jQuery (window loaded event) * - Wallpaper Engine Web Wallpaper environment - * - audio-processing supported wallpaper... - * - * This is an aditional JS file to be included in any Wallpaper Engine - * Web-Wallpaper project to make working with audio easier. - * It will automatically start to receive and process the audio data - * which can then be accessed on the global object. - * - * @todo - * - use worker run instead of multiple messages + * - audio-processing supported web wallpaper... * */ From 27b5ea18d7f77a214d0498a974b94f9da49b053e Mon Sep 17 00:00:00 2001 From: hexxone Date: Mon, 1 Feb 2021 19:08:52 +0100 Subject: [PATCH 24/76] Fix logging --- src/WEWWA.ts | 2 +- src/wasc-worker | 2 +- src/weas/WEAS.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WEWWA.ts b/src/WEWWA.ts index aa1739e..1b7e69c 100644 --- a/src/WEWWA.ts +++ b/src/WEWWA.ts @@ -648,7 +648,7 @@ export class WEWWA { for (var p in wewwaProps) { var prop = wewwaProps[p]; - // some eval magic + // some ev(a|i)l magic var visible = true; if (prop.condition != null) { // copy our condition string to modify diff --git a/src/wasc-worker b/src/wasc-worker index cd4efae..4d66da2 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit cd4efae23b8855034dabd7470718340818d9c03b +Subproject commit 4d66da212227491306dcaa9325cb7750c106adf2 diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 522163b..320f6bc 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -194,11 +194,11 @@ export class WEAS extends CComponent { const transfer = importObject.__getFloat64ArrayView(exports.audioSettings); transfer.set(data); - - console.debug("Send Settings to Worker: " + JSON.stringify(data)); }, { // Data passed to worker data: sett + }).then(() => { + Smallog.Debug("Sent Settings to WEAS: " + JSON.stringify(sett)); }); } From ba83d6b07e70c10ef8c1e31b29cbb2e6ff27d32e Mon Sep 17 00:00:00 2001 From: hexxone Date: Tue, 2 Feb 2021 23:56:13 +0100 Subject: [PATCH 25/76] Submodule changes --- src/wasc-worker | 2 +- src/weas/WEAS.ts | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/wasc-worker b/src/wasc-worker index 4d66da2..4629c0c 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 4d66da212227491306dcaa9325cb7750c106adf2 +Subproject commit 4629c0c86c27ad4e9b9362ef3ede018026ced143 diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 320f6bc..8dd7ab8 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -24,7 +24,7 @@ import { CSettings } from "../CSettings"; import { Ready } from "../Ready"; import { Smallog } from "../Smallog"; -import wascModule from '../wasc-worker'; +import WascInit from '../wasc-worker'; const DAT_LEN = 128; @@ -107,10 +107,9 @@ export class WEAS extends CComponent { var self = this; - this.weasModule = await wascModule('WEAS.wasm', {}, true); + this.weasModule = await WascInit('WEAS.wasm'); const { run } = this.weasModule; - // pass settings to module this.updateSettings(); From bf813847a50af3389dbdb435c35cd5ea07363e51 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Wed, 24 Feb 2021 16:52:32 +0100 Subject: [PATCH 26/76] refactoring & typization --- src/CComponent.ts | 41 ++++++----- src/CSettings.ts | 2 +- src/ReloadHelper.ts | 7 +- src/Smallog.ts | 7 +- src/WEICUE.ts | 60 ++++++++-------- src/WEWWA.ts | 84 +++++++++++----------- src/WarnHelper.ts | 47 +++++++++++-- src/wasc-worker | 2 +- src/weas/WEAS.ts | 49 +++++++------ src/weas/WEAS.wasm.asc | 156 +++++++++++++++++++++-------------------- 10 files changed, 257 insertions(+), 198 deletions(-) diff --git a/src/CComponent.ts b/src/CComponent.ts index 10f60eb..44b0979 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -12,33 +12,42 @@ */ import { CSettings } from "./CSettings"; +import { Smallog } from "./Smallog"; export class CComponent { + private needsUpdate = false; + public settings: CSettings = null; // Important: Append your child objects, for settings to be applied correctly! public children: CComponent[] = []; - public GetComponents() { - var list: CComponent[] = [this]; - this.children.forEach((ch) => list.push(...ch.GetComponents())); - return list; + + // will recursively try to set a setting with type and return success. + // will also flag the module as "needsUpdate" + public ApplySetting(key: any, value: any): boolean { + var found = this.settings.apply(key, value); + if (found) { + this.needsUpdate = true; + Smallog.Debug(`ApplySetting: ${key}:${value}`); + } + this.children.forEach(ch => found ||= ch.ApplySetting(key, value)); + return found; } - public GetSettings() { - var list: CSettings[] = []; - this.GetComponents().forEach(co => list.push(co.settings)); - return list; + // will recursively update all needed modules afffter settings changes + // DO NOT OVERWWRITE !!! + public UpdateAll() { + this.children.forEach(c => c.UpdateAll()); + if (this.needsUpdate) this.UpdateSettings(); + this.needsUpdate = false; } - // WARNING: this merely returns an object copy!! Has no method members!! - public GetSettingsObj() { - let result = {} - Object.getOwnPropertyNames(this.settings).forEach(p => { - if (typeof this.settings[p] == "function") return; - result[p] = this.settings[p]; - }); - return result; + // NEEDS TO BE OVERWRITTEN FOR DOING ACTIONS ON SETTINGS CHANGE + // should usually get called automatically when needed.. no need for extra calling + public UpdateSettings(): Promise { + console.error(`ERROR_NO_IMPL at: CComponent.UpdateSettings!\r\nPlease override this method!`) + return; } } \ No newline at end of file diff --git a/src/CSettings.ts b/src/CSettings.ts index 2a05f10..06a47b7 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -24,7 +24,7 @@ export class CSettings { this[key] = castedValue; return true; } - else Smallog.Error("ISettings Error: invalid type on: '" + key + + else Smallog.Error("CSettings Error: invalid type on: '" + key + "'. Is: '" + typeof this[key] + "', applied: '" + typeof castedValue + "'"); } return false; diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index b52cb9c..debecda 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -11,6 +11,7 @@ * */ +import { CComponent } from "./CComponent"; import { CSettings } from "./CSettings"; import { Ready } from "./Ready"; @@ -18,11 +19,12 @@ class ReloadSettings extends CSettings { reload_seconds: number = 3; } -export class ReloadHelper { +export class ReloadHelper extends CComponent { public settings: ReloadSettings = new ReloadSettings(); constructor() { + super(); Ready().then(() => { this.injectCSS(); this.injectHTML(); @@ -94,4 +96,7 @@ export class ReloadHelper { public Hide() { $("#reload-bar, #reload-text").removeClass("show").addClass("done"); } + + // dont print IMPL error + public UpdateSettings(): Promise { return; } }; \ No newline at end of file diff --git a/src/Smallog.ts b/src/Smallog.ts index 739358c..5a19092 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -3,7 +3,7 @@ */ -function TraceCall(def: string) { +export function TraceCall(def: string, depth: number = 3) { try { throw new Error("TraceCall()"); } @@ -11,7 +11,7 @@ function TraceCall(def: string) { // Examine e.stack here if (e.stack) { const splt = e.stack.split(/\n/); - if (splt.length > 3) return "[" + splt[3].trim().substring(3) + "] "; + if (splt.length > depth) return "[" + splt[depth].trim().substring(3) + "] "; } } return def; @@ -25,7 +25,8 @@ export enum LogLevel { export module Smallog { - var logLevel: LogLevel = LogLevel.Info; + var logLevel: LogLevel = LogLevel.Debug; // todo level Info for release + var preFix: string = "[Smallog] "; var printTime: boolean = false; diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 4498ff0..7a848b7 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -290,7 +290,7 @@ AAAASUVORK5CYII= } // show a message by icue - icueMessage(msg) { + private icueMessage(msg) { Smallog.Debug("MSG: " + msg, ClassName); $("#icueholder").css('opacity', 1); $("#icuetext").html(msg); @@ -303,7 +303,7 @@ AAAASUVORK5CYII= } // helper - getArea(inPx?) { + private getArea(inPx?) { var sett = this.settings; var wwid = window.innerWidth; var whei = window.innerHeight; @@ -320,7 +320,7 @@ AAAASUVORK5CYII= } // get data for icue - getEncodedCanvasImageData(imageData) { + private getEncodedCanvasImageData(imageData) { var colorArray = []; for (var d = 0; d < imageData.data.length; d += 4) { var write = d / 4 * 3; @@ -332,7 +332,7 @@ AAAASUVORK5CYII= } // canvas blur helper function - gBlurCanvas(canvas, ctx, blur) { + private gBlurCanvas(canvas, ctx, blur) { var sum = 0; var delta = 5; var alpha_left = 1 / (2 * Math.PI * delta * delta); @@ -354,26 +354,7 @@ AAAASUVORK5CYII= ctx.globalAlpha = 1; } - // show or hide preview - updatePreview() { - var sett = this.settings; - // create preview? - if (!this.preview && sett.icue_area_preview) { - this.preview = document.createElement("div"); - this.preview.classList.add("cuePreview"); - document.body.appendChild(this.preview); - } - // update settings or destroy - if (this.preview) { - if (!sett.icue_area_preview) { - document.body.removeChild(this.preview); - this.preview = null; - } - else Object.assign(this.preview.style, this.getArea(true)); - } - } - - init() { + private init() { var sett = this.settings; // dont initialize if disabled if (sett.icue_mode == 0) return; @@ -402,9 +383,30 @@ AAAASUVORK5CYII= // update devices about every 33ms/30fps. iCue doesnt really support higher values this.icueInterval = setInterval(() => this.updateFrame(), 1000 / 30); } + + + // show or hide preview + public UpdateSettings(): Promise { + var sett = this.settings; + // create preview? + if (!this.preview && sett.icue_area_preview) { + this.preview = document.createElement("div"); + this.preview.classList.add("cuePreview"); + document.body.appendChild(this.preview); + } + // update settings or destroy + if (this.preview) { + if (!sett.icue_area_preview) { + document.body.removeChild(this.preview); + this.preview = null; + } + else Object.assign(this.preview.style, this.getArea(true)); + } + return; + } // will initialize ICUE api & usage - initCUE(count) { + private initCUE(count) { // wait for plugins if (!this.isAvailable) { if (count < 100) { @@ -433,7 +435,7 @@ AAAASUVORK5CYII= } // do the thing... - updateFrame() { + private updateFrame() { var sett = this.settings; if (this.PAUSED || !this.isAvailable || sett.icue_mode == 0 || this.icueDevices.length < 1) return; // projection mode @@ -472,7 +474,7 @@ AAAASUVORK5CYII= } // prepare canvas - updateCanvas(mainCanvas) { + public updateCanvas(mainCanvas) { var sett = this.settings; if (!this.isAvailable || !mainCanvas || sett.icue_mode == 0 || this.icueDevices.length < 1) return; @@ -493,13 +495,13 @@ AAAASUVORK5CYII= } } - showWaiting() { + private showWaiting() { $("#icuetext").html("LED: waiting for plugin."); $("#icueholder").fadeIn({ queue: false, duration: "fast" }); $("#icueholder").animate({ top: "0px" }, "fast"); } - hideWaiting() { + private hideWaiting() { $("#icueholder").fadeOut({ queue: false, duration: "fast" }); $("#icueholder").animate({ top: "-120px" }, "fast"); } diff --git a/src/WEWWA.ts b/src/WEWWA.ts index 1b7e69c..afdbedd 100644 --- a/src/WEWWA.ts +++ b/src/WEWWA.ts @@ -49,8 +49,10 @@ import { Ready } from "./Ready"; import { Smallog } from "./Smallog"; import { OfflineHelper } from "./offline/OfflineHelper"; import { CC } from "cookieconsent"; +import { myFetch } from "./wasc-worker/WascRT"; const LogHead = "[WEWWA] "; +const DefLang = "de-de"; export class WEWWA { @@ -86,7 +88,7 @@ export class WEWWA { // intialize when ready Ready().then(() => { if (CC) { /* This tells the compiler to include CookieConsent at this point. */ } - const cc = window['cookieconsent'].initialise({ + window['cookieconsent'].initialise({ palette: { popup: { background: "#000" }, button: { background: "#f1d600" } @@ -100,12 +102,17 @@ export class WEWWA { // continue initializing finished(); this.Init(); + + // pause and resume on focus events + window.onblur = () => this.SetPaused(true); + window.onfocus = () => this.SetPaused(false); }); }); } private Init() { - this.LoadProjectJSON((proj) => { + myFetch("project.json", "json").then(proj => { + if (proj.type != "web") { Smallog.Error("Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...", LogHead); return; @@ -114,20 +121,11 @@ export class WEWWA { this.LoadStorage(); this.AddStyle(); this.AddMenu(localStorage.getItem("wewwaLang")); - this.UpdateSettings(); + this.EvaluateSettings(); this.ApplyProp(proj.general.properties); }); } - private LoadProjectJSON(complete) { - $.ajax({ - url: "project.json", - beforeSend: (xhr) => xhr.overrideMimeType("text/plain;"), - success: (result) => complete(JSON.parse(result)), - error: (xhr, status, error) => Smallog.Error(status + ": ajax error!\r\n" + error, LogHead) - }); - } - private LoadStorage() { var props = this.project.general.properties; var last = localStorage.getItem("wewwaLastProps"); @@ -249,7 +247,6 @@ export class WEWWA { } private AddMenu(lang) { - var self = this; if (this.htmlMenu) { document.body.removeChild(this.htmlMenu); @@ -297,7 +294,7 @@ export class WEWWA { aBtn1.classList.add("orange"); aBtn1.innerHTML = "Microphone"; aBtn1.addEventListener("click", e => { - self.requestMicrophone(); + this.requestMicrophone(); }); var aBtn2 = ce("a"); @@ -305,7 +302,7 @@ export class WEWWA { aBtn2.innerHTML = "Select URL"; aBtn2.addEventListener("click", e => { var uri = prompt("Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!", "https://example.com/test.mp3"); - self.initiateAudio(uri); + this.initiateAudio(uri); }); var aBtn3 = ce("input"); @@ -315,7 +312,7 @@ export class WEWWA { aBtn3.addEventListener("change", e => { var file = e.target.files[0]; if (!file) return; - self.initiateAudio(file); + this.initiateAudio(file); }); td1.append(aBtn1, aBtn2, aBtn3); @@ -337,8 +334,8 @@ export class WEWWA { dropArea.addEventListener("drop", e => { e.stopPropagation(); e.preventDefault(); - var droppedFiles = e.dataTransfer.files; - self.initiateAudio(droppedFiles[0]); + const droppedFiles = e.dataTransfer.files; + this.initiateAudio(droppedFiles[0]); }, false); dropt1.append(dropArea); dropRow.append(dropt1, dropt2); @@ -351,7 +348,7 @@ export class WEWWA { hrstop.classList.add("red"); hrstop.innerHTML = "Stop All Audio"; hrstop.addEventListener("click", e => { - self.stopAudioInterval(); + this.stopAudioInterval(); }); var hrhr = ce("hr") hrtd1.id = "audioMarker"; @@ -372,7 +369,7 @@ export class WEWWA { var local = proj.general.localization; if (local) { // set default language - if (!lang) lang = "en-us"; + if (!lang) lang = DefLang; // add html struct var row = ce("tr"); var td1 = ce("td"); @@ -405,10 +402,11 @@ export class WEWWA { } } // if changed, do it all over again. + const self = this; lan.addEventListener("change", function (e) { localStorage.setItem("wewwaLang", this.value); self.AddMenu(this.value); - self.UpdateSettings(); + self.EvaluateSettings(); (self.htmlIcon as any).click(); }); td2.setAttribute("colspan", 2); @@ -568,26 +566,27 @@ export class WEWWA { public SetProperty(prop, elm) { + // get the type and apply the value var props = this.project.general.properties; + // check for legit setting... if (!props[prop]) { Smallog.Error("SetProperty name not found: " + prop, LogHead); return; } + // enabled delayed call of settings update var applyCall = (val) => { // save the updated value to storage props[prop].value = val; - - //Smallog.Debug("Property set: " + prop + " v: " + val); - // update - this.UpdateSettings(); + this.EvaluateSettings(); var obj = {}; obj[prop] = props[prop]; this.ApplyProp(obj); }; + // process value based on DOM element type switch (props[prop].type) { case "bool": @@ -621,28 +620,18 @@ export class WEWWA { // this way we can easily store whole files in the configuration & localStorage. // its not safe that this works with something else than image files. private XHRLoadAndSaveLocal(url, resCall) { - // Create XHR and FileReader objects - var xhr = new XMLHttpRequest(); - var fileReader = new FileReader(); - xhr.open("GET", url, true); - xhr.responseType = "blob"; - xhr.addEventListener("load", function () { - if (xhr.status == 200) { - // onload needed since Google Chrome doesn't support addEventListener for FileReader - fileReader.onload = function (evt) { - // Read out file contents as a Data URL - resCall(evt.target.result) - }; - // Load blob as Data URL - fileReader.readAsDataURL(xhr.response); - } - }, false); - // Send XHR - xhr.send(); + myFetch(url, "blob").then(resp => { + // Read out file contents as a Data URL + const fReader = new FileReader(); + // onload needed since Google Chrome doesn't support addEventListener for FileReader + fReader.onload = (evt) => resCall(evt.target.result) + // Load blob as Data URL + fReader.readAsDataURL(resp); + }); } - private UpdateSettings() { + public EvaluateSettings() { var wewwaProps = this.project.general.properties; localStorage.setItem("wewwaLastProps", JSON.stringify(wewwaProps)); for (var p in wewwaProps) { @@ -711,6 +700,13 @@ export class WEWWA { } } + public SetPaused(val: boolean) { + var wpl = window['wallpaperPropertyListener']; + if (wpl && wpl.setPaused) { + wpl.setPaused(val); + } + } + private rgbToHex(rgb) { function cth(c) { var h = Math.floor(c * 255).toString(16); diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index eac3b4b..55683cd 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -22,6 +22,7 @@ const ELM_ID = "triggerwarn"; // TODO test getting text class WarnSettings extends CSettings { + seizure_warning: boolean = true; seizure_text: string = "/* */"; animate_seconds: number = 1; wait_seconds: number = 10; @@ -33,6 +34,9 @@ export class WarnHelper extends CComponent { private element: HTMLDivElement; + // promise behind showing the warning + private showResolve: any; + constructor() { super(); @@ -66,19 +70,48 @@ export class WarnHelper extends CComponent { document.body.append(this.element); } - public Show() { + private setText() { + this.element.innerHTML = this.settings.seizure_text.replace("\r\n", "
"); + } + + public Show(): Promise { return new Promise(resolve => { + // dont show + if (!this.settings.seizure_warning) { + resolve(); + return; + } + // wait for resolve by "Hide()" + this.showResolve = resolve; // make text - const txtt = this.settings.seizure_text.replace("\r\n", "
"); - this.element.innerHTML = txtt; + this.setText(); // show it this.element.classList.add("show"); // wait some time + setTimeout(this.Hide, this.settings.wait_seconds * 1000); + }); + } + + public Hide(): Promise { + return new Promise(resolve => { + // hide it & wait + this.element.classList.remove("show"); setTimeout(() => { - // hide it & wait again - this.element.classList.remove("show"); - setTimeout(resolve, this.settings.animate_seconds * 1000); - }, this.settings.wait_seconds * 1000); + if(this.showResolve) this.showResolve(); + this.showResolve = null; + resolve(); + }, this.settings.animate_seconds * 1000); + }); } + + public UpdateSettings(): Promise { + // update text + this.setText(); + // fix for instantly removing the warning while it shows + if(!this.settings.seizure_warning && this.element.classList.contains("show")) + return this.Hide(); + // whatever + return; + } }; \ No newline at end of file diff --git a/src/wasc-worker b/src/wasc-worker index 4629c0c..e8d9269 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 4629c0c86c27ad4e9b9362ef3ede018026ced143 +Subproject commit e8d9269fe3adf8d5fed3ba2a8bcd7b2543f71323 diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 8dd7ab8..38ec5d0 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -19,6 +19,7 @@ * */ +import { ASUtil } from "@assemblyscript/loader"; import { CComponent } from "../CComponent"; import { CSettings } from "../CSettings"; import { Ready } from "../Ready"; @@ -49,6 +50,8 @@ export class WEASettings extends CSettings { bass_multiplier: number = 1.8; // ignore value leveling for "silent" data minimum_volume: number = 0.005; + // use low latency audio? + low_latency: boolean = false; } enum Sett { @@ -107,16 +110,16 @@ export class WEAS extends CComponent { var self = this; - this.weasModule = await WascInit('WEAS.wasm'); + this.weasModule = await WascInit('WEAS.wasm', {}, !this.settings.low_latency); const { run } = this.weasModule; // pass settings to module - this.updateSettings(); + await this.UpdateSettings(); // register audio callback on module window['wallpaperRegisterAudioListener'](async audioArray => { // check proof - if (!audioArray) return; + if (!audioArray || !this.settings.audioprocessing) return; if (audioArray.length != DAT_LEN) { Smallog.Error("audioListener: received invalid audio data array. Length: " + audioArray.length); return; @@ -130,19 +133,16 @@ export class WEAS extends CComponent { run(({ module, instance, importObject, params }) => { const { exports } = instance; const { data } = params[0]; + const io = importObject as ASUtil; - //console.debug("Send Data to Worker: " + JSON.stringify(data)); - - // apply data to module memory - const transfer = importObject.__getFloat64ArrayView(exports.inputData); - transfer.set(data); - // actual audio data processing + // set audio data directly in module memory + io.__getFloat64ArrayView(exports.inputData).set(data); + // trigger processing processing exports.update(); - - // get updates Data & Properties + // get copy of updated Data & Properties return { - data: importObject.__getFloat64ArrayView(exports.outputData), - props: importObject.__getFloat64ArrayView(exports.audioProps), + data: new Float64Array(io.__getFloat64ArrayView(exports.outputData)), + props: new Float64Array(io.__getFloat64ArrayView(exports.audioProps)), } }, { // params passed to worker @@ -162,6 +162,7 @@ export class WEAS extends CComponent { }); } + // converts calculated output property number-array to string-associative-array private getProps(dProps: ArrayLike) { var keys = Object.keys(Props); keys = keys.slice(keys.length / 2); @@ -174,7 +175,7 @@ export class WEAS extends CComponent { } // CAVEAT: only available after init and module load - public async updateSettings() { + public UpdateSettings(): Promise { if (!this.weasModule) return; const { run } = this.weasModule; @@ -187,12 +188,11 @@ export class WEAS extends CComponent { } // WRAP IN isolated Function ran inside worker - await run(({ module, instance, importObject, params }) => { + return run(({ module, instance, importObject, params }) => { const { exports } = instance; const { data } = params[0]; - - const transfer = importObject.__getFloat64ArrayView(exports.audioSettings); - transfer.set(data); + const io = importObject as ASUtil; + io.__getFloat64ArrayView(exports.audioSettings).set(data); }, { // Data passed to worker data: sett @@ -201,9 +201,16 @@ export class WEAS extends CComponent { }); } + /** + * return false if: + * - processing is disabled + * - there is no data + * - the data is silent + * - data is too old (> 3 + */ public hasAudio() { - // return false if there is no data or its invalid due to time (> 3s old) - return this.lastAudio && !this.lastAudio.silent && - (performance.now() / 1000 - this.lastAudio.time < 3); + return this.settings.audioprocessing + && this.lastAudio && this.lastAudio.silent == 0 + && (performance.now() / 1000 - this.lastAudio.time < 3); } } \ No newline at end of file diff --git a/src/weas/WEAS.wasm.asc b/src/weas/WEAS.wasm.asc index fc748e6..dc5212f 100644 --- a/src/weas/WEAS.wasm.asc +++ b/src/weas/WEAS.wasm.asc @@ -41,9 +41,9 @@ function deallocArray(arr: T[]): void { memory.free(changetype(arr)); } - ////////////////////////// - // Main Program - ////////////////////////// +////////////////////////// +// Main Program +////////////////////////// const DAT_LEN: i32 = 128; const HLF_LEN: i32 = 64; @@ -62,125 +62,138 @@ const pNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.637679025 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, 2.4043566176474, 2.4280476777842, 2.3917477397336, 2.4032522546622, 2.3614180150678]; + const pinkNoise = new Float64Array(HLF_LEN); pinkNoise.set(pNoise); +const tempArr = new Float64Array(DAT_LEN); + +var i: i32 = 0; +var indx: i32 = 0; +var tmpI: i32 = 0; + +var tmpF: f64 = 0; +var oldMax: f64 = 0; +var newMax: f64 = 0; +var sum: f64 = 0; +var min: f64 = 1; +var max: f64 = 0; +var bass: f64 = 0; +var mids: f64 = 0; +var peaks: f64 = 0; +var diff: f64 = 0; +var mlt: f64 = 0; +var average: f64 = 0; +var silent: f64 = 0; +var intensity: f64 = 0; +var range: f64 = 0; // correct pink noise for first and second half function correctPinkNoise(data: Float64Array): void { - var correct = new Float64Array(DAT_LEN); - for (var i = 0; i < HLF_LEN; i++) { - correct[i] = data[i] / pinkNoise[i]; - correct[HLF_LEN + i] = data[HLF_LEN + i] / pinkNoise[i]; + for (i = 0; i < HLF_LEN; i++) { + tempArr[i] = data[i] / pinkNoise[i]; + tempArr[HLF_LEN + i] = data[HLF_LEN + i] / pinkNoise[i]; } - data.set(correct); + data.set(tempArr); } // merge first and second half into full range function stereoToMono(data: Float64Array): void { - var mono = new Float64Array(DAT_LEN); - var mIdx = 0; - for (var i = 0; i < HLF_LEN; i++) { - mono[mIdx++] = data[i]; - mono[mIdx++] = data[HLF_LEN + i]; + tmpI = 0; + for (i = 0; i < HLF_LEN; i++) { + tempArr[tmpI++] = data[i]; + tempArr[tmpI++] = data[HLF_LEN + i]; } - data.set(mono); + data.set(tempArr); } // switch front with back in first half function invertFirst(data: Float64Array): void { - for (var i = 0; i < QRT_LEN; i++) { - var a = data[i]; + for (i = 0; i < QRT_LEN; i++) { + tmpF = data[i]; data[i] = data[HLF_LEN - 1 - i]; - data[HLF_LEN - 1 - i] = a; + data[HLF_LEN - 1 - i] = tmpF; } } // switch front with back in second half function invertSecond(data: Float64Array): void { - for (var i = 0; i < QRT_LEN; i++) { - var b = data[HLF_LEN + i]; + for (i = 0; i < QRT_LEN; i++) { + tmpF = data[HLF_LEN + i]; data[HLF_LEN + i] = data[DAT_LEN - 1 - i]; - data[DAT_LEN - 1 - i] = b; + data[DAT_LEN - 1 - i] = tmpF; } } // switch front with back in full range function invertAll(data: Float64Array): void { - for (var i = 0; i < HLF_LEN; i++) { - var a = data[i]; + for (i = 0; i < HLF_LEN; i++) { + tmpF = data[i]; data[i] = data[DAT_LEN - 1 - i]; - data[DAT_LEN - 1 - i] = a; + data[DAT_LEN - 1 - i] = tmpF; } } // filter peaks for full range function peakFilter(array: Float64Array, amount: f64): void { - var oldMax: f64 = 0; - var newMax: f64 = 0; - var newArray = new Float64Array(DAT_LEN); - var i = 0; + oldMax = 0; + newMax = 0; // pow this shit - for (; i < DAT_LEN; i++) { + for (i = 0; i < DAT_LEN; i++) { if (array[i] > oldMax) oldMax = array[i]; - newArray[i] = Math.pow(array[i] * amount, amount) as f64; - if (newArray[i] > newMax) newMax = newArray[i]; + tempArr[i] = Math.pow(array[i] * amount, amount) as f64; + if (tempArr[i] > newMax) newMax = tempArr[i]; } // re-scale & apply - var divide: f64 = newMax / oldMax; + tmpF = newMax / oldMax; for (i = 0; i < DAT_LEN; i++) - array[i] = newArray[i] / divide; + array[i] = tempArr[i] / tmpF; } // smooth values for full range function smoothArray(array: Float64Array, steps: i32): void { - var newArray = new Float64Array(DAT_LEN); // make smoothed array - for (var outer = 0; outer < DAT_LEN; outer++) { - var sum: f64 = 0; - for (var inner = outer - steps; inner <= outer + steps; inner++) { + for (i = 0; i < DAT_LEN; i++) { + tmpF = 0; + for (var inner = i - steps; inner <= i + steps; inner++) { var idx = inner; if (idx < 0) idx += DAT_LEN; - sum += array[idx % DAT_LEN]; + tmpF += array[idx % DAT_LEN]; } - newArray[outer] = sum / (((steps * 2) + 1) as f64); + tempArr[i] = tmpF / (((steps * 2) + 1) as f64); } - array.set(newArray); + array.set(tempArr); } // function will apply setting-defined data smoothing function applyValueLeveling(curr: Float64Array, prev: Float64Array, inc: f64, dec: f64): void { - for (var i = 0; i < DAT_LEN; i++) { - var diff: f64 = curr[i] - prev[i]; - var mlt: f64 = 100 - (diff > 0 ? inc : dec); + for (i = 0; i < DAT_LEN; i++) { + diff = curr[i] - prev[i]; + mlt = 100 - (diff > 0 ? inc : dec); curr[i] -= diff * mlt / 100; } } +// check helper +function isOn(a: f64): boolean { + return a > 0; +} + + // THEW INPUT VALUE TO WRITE TO export const inputData = new Float64Array(DAT_LEN); -inputData.fill(0.0); // this will hold the current processed audio data // either: B-H | H-B | HL-BL-BR-HR | BL-HL-HR-BR // where ( B=bass, H=high, L=left, R=right ) // in range > 0.0 and ~< 1.5 export const outputData = new Float64Array(DAT_LEN); -outputData.fill(0.0); // this will hold the current processed properties: export const audioProps = new Float64Array(10); -audioProps.fill(0.0); // this will hold the current processing settings export const audioSettings = new Float64Array(11); -audioSettings.fill(0.0); - - -// check helper -function isOn(a: f64): boolean { - return a > 0; -} // this will update and process new data export function update(): void { @@ -219,44 +232,37 @@ export function update(): void { if (isOn(audioSettings[4])) smoothArray(inputData, Math.floor(audioSettings[4]) as i32); - //logF64Array(inputData); - //logF64Array(outputData); - // process with last data applyValueLeveling(inputData, outputData, audioSettings[5], audioSettings[6]); - // process current frequency data and previous if given - var sum: f64 = 0, - min: f64 = 1, - max: f64 = 0, - bass: f64 = 0, - mids: f64 = 0, - peaks: f64 = 0; + // process current frequency data and previous + sum = max = bass = mids = peaks = 0; + min = 1; - for (var indx = 0; indx < DAT_LEN; indx++) { + for (indx = 0; indx < DAT_LEN; indx++) { // parse current freq value - var idata: f64 = inputData[indx]; + tmpF = inputData[indx]; // process min max value - if (idata < min) min = idata; - if (idata > max) max = idata; + if (tmpF < min) min = tmpF; + if (tmpF > max) max = tmpF; // process ranges - if (indx < (42 / 3)) bass += idata * audioSettings[9]; - else if (indx > 69) peaks += idata * audioSettings[7]; - else mids += idata * audioSettings[8]; + if (indx < (42 / 3)) bass += tmpF * audioSettings[9]; + else if (indx > 69) peaks += tmpF * audioSettings[7]; + else mids += tmpF * audioSettings[8]; // calc peak average - sum += idata; + sum += tmpF; } // calc average with previous entry - var average: f64 = sum / (DAT_LEN as f64); - var silent: f64 = (max < audioSettings[10] / 1000) ? 0.9999 : 0.00; - var intensity: f64 = (bass * 8 - mids + peaks) / 6 / average; - var range: f64 = max - min; + average = sum / (DAT_LEN as f64); + silent = (max < audioSettings[10] / 1000) ? 0.9999 : 0.00; + intensity = (bass * 8 - mids + peaks) / 6 / average; + range = max - min; // Apply Data outputData.set(inputData); - audioProps.set([bass,mids,peaks,sum,min,max,average,range,silent,intensity]) + audioProps.set([bass, mids, peaks, sum, min, max, average, range, silent, intensity]) // DONE } From 6f540efea4675052cd0a6be581665b056c6ea75a Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Wed, 17 Mar 2021 14:43:59 +0100 Subject: [PATCH 27/76] removed jQuery --- src/ReloadHelper.ts | 27 ++++++---- src/{Ready.ts => Util.ts} | 6 ++- src/WEICUE.ts | 74 ++++++++++++++------------- src/WEWWA.ts | 104 +++++++++++++++++++++++--------------- src/WarnHelper.ts | 2 +- src/weas/WEAS.ts | 2 +- 6 files changed, 123 insertions(+), 92 deletions(-) rename src/{Ready.ts => Util.ts} (90%) diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index debecda..812c126 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -13,7 +13,7 @@ import { CComponent } from "./CComponent"; import { CSettings } from "./CSettings"; -import { Ready } from "./Ready"; +import { Ready } from "./Util"; class ReloadSettings extends CSettings { reload_seconds: number = 3; @@ -41,12 +41,12 @@ export class ReloadHelper extends CComponent { height: 10px; width: 0%; background-color: #989a; - transition: all ${this.settings.reload_seconds}s ease, opacity 0.33s ease; } #reload-bar.show { opacity: 1; width: 100%; background-color: #e11a; + transition: all ${this.settings.reload_seconds}s ease, opacity 0.33s ease; } #reload-bar.done { transition: opacity 0.33s ease; @@ -59,12 +59,12 @@ export class ReloadHelper extends CComponent { font-weight: 100; font-size: 3em; color: #fffa; - transition: all .33s ease, color ${this.settings.reload_seconds}s ease, text-shadow ${this.settings.reload_seconds}s ease; } #reload-text.show { top: 10px; color: #e11a; text-shadow: 0 0 20px rgba(255, 50, 50, .5), 0 0 15px rgba(255, 50, 50, .5); + transition: all .33s ease, color ${this.settings.reload_seconds}s ease, text-shadow ${this.settings.reload_seconds}s ease; } #reload-text.done { transition: position 0.33s linear; @@ -78,7 +78,7 @@ export class ReloadHelper extends CComponent { private injectHTML() { var outer = document.createElement("div"); - outer.id = "reloader"; + outer.id = "reloadhelper"; var bar = document.createElement("div"); bar.id = "reload-bar"; var tex = document.createElement("h1"); @@ -88,13 +88,18 @@ export class ReloadHelper extends CComponent { document.body.append(outer); } - // @Todo make bar always reset to 0 on show - public Show() { - $("#reload-bar, #reload-text").removeClass("done").addClass("show"); - } - - public Hide() { - $("#reload-bar, #reload-text").removeClass("show").addClass("done"); + // @Todo test: bar always reset to 0 on show ?? + public Show(visible: boolean) { + const e1 = document.getElementById("reload-bar"); + const e2 = document.getElementById("reload-text"); + if(visible) { + e1.classList.add("show"); + e2.classList.add("show"); + } + else { + e1.classList.remove("show"); + e2.classList.remove("show"); + } } // dont print IMPL error diff --git a/src/Ready.ts b/src/Util.ts similarity index 90% rename from src/Ready.ts rename to src/Util.ts index 28e65db..7fb26ee 100644 --- a/src/Ready.ts +++ b/src/Util.ts @@ -19,4 +19,8 @@ export function Ready() { // Otherwise, wait until document is loaded document.addEventListener('DOMContentLoaded', resolve, false); }); -} \ No newline at end of file +} + +export function ToggleClass(id: string, clas: string) { + +} \ No newline at end of file diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 7a848b7..418b7ba 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -20,13 +20,15 @@ import { CComponent } from "./CComponent"; import { CSettings } from "./CSettings"; -import { Ready } from "./Ready"; +import { Ready } from "./Util"; import { Smallog } from "./Smallog"; import { WEAS } from "./weas/WEAS"; const ClassName: string = "[WEICUE] "; const canvasX: number = 23; const canvasY: number = 7; +const WaitTime: number = 30; +const Transition: number = 3; export class CUESettings extends CSettings { icue_mode: number = 1; @@ -45,9 +47,13 @@ export class CUESettings extends CSettings { export class WEICUE extends CComponent { private weas: WEAS = null; + + private holder: HTMLDivElement = null; + private texter: HTMLDivElement = null; private preview: HTMLDivElement = null; private helperCanvas: HTMLCanvasElement = null; private helperContext: CanvasRenderingContext2D = null; + private icueDevices = []; private icueInterval = null; @@ -81,13 +87,22 @@ export class WEICUE extends CComponent { var st = document.createElement("style"); st.innerHTML = ` #icueholder { + opacity: 0; position: absolute; top: -120px; left: 0; width: auto; height: auto; margin: 10px; - display: none; + transition: all ${Transition}s ease; + } + #icueholder.show { + opacity: 1; + top: 0px; + } + #icueholder.waiting { + opacity: 0.2; + transition: all 10s ease; } #icuelogo { float: left; @@ -111,8 +126,10 @@ export class WEICUE extends CComponent { } private injectHTML() { - var outer = document.createElement("div"); - outer.id = "icueholder"; + // create container + this.holder = document.createElement("div"); + this.holder.id = "icueholder"; + // create icon (no ref needed) var imgg = document.createElement("img"); imgg.id = "icuelogo"; // @TODO remove this abomination @@ -282,24 +299,27 @@ qPiqkr4Ha7SnT/gajTjh58gV7SNEge0B9oWG/a1i9UwO3Cm2KY4hKvg825c8VP+xfIT5KomTQiuMBC/h AAAASUVORK5CYII= `); // make text holder - var txtt = document.createElement("div"); - txtt.id = "icuetext"; + this.texter = document.createElement("div"); + this.texter.id = "icuetext"; // append image and text - outer.append(imgg, txtt); - document.body.append(outer); + this.holder.append(imgg, this.texter); + document.body.append(this.holder); } // show a message by icue - private icueMessage(msg) { + private icueMessage(msg, waiting :boolean = false) { Smallog.Debug("MSG: " + msg, ClassName); - $("#icueholder").css('opacity', 1); - $("#icuetext").html(msg); - $("#icueholder").fadeIn({ queue: false, duration: "slow" }); - $("#icueholder").animate({ top: "0px" }, "slow"); + // set text + this.texter.innerHTML = msg; + // show + this.holder.classList.add("show"); + if(waiting) this.holder.classList.add("waiting"); + // hide again + const waiTime = waiting ? (WaitTime) : (Transition * 1000 + 4000); setTimeout(() => { - $("#icueholder").fadeOut({ queue: false, duration: "slow" }); - $("#icueholder").animate({ top: "-120px" }, "slow"); - }, 10000); + this.holder.classList.remove("show"); + if(waiting) this.holder.classList.remove("waiting"); + }, waiTime); } // helper @@ -359,13 +379,8 @@ AAAASUVORK5CYII= // dont initialize if disabled if (sett.icue_mode == 0) return; - this.showWaiting(); - setTimeout(() => { - this.hideWaiting(); - }, 30000); - + this.icueMessage("LED: waiting for plugin.", true); this.initCUE(0); - Smallog.Debug("init...", ClassName); // recreate if reinit @@ -409,10 +424,7 @@ AAAASUVORK5CYII= private initCUE(count) { // wait for plugins if (!this.isAvailable) { - if (count < 100) { - $("#icueholder").animate({ 'opacity': 0.1 + (100 - count) / 115 }, 250); - setTimeout(() => this.initCUE(count + 1), 300); - } + if (count < 100) setTimeout(() => this.initCUE(++count), 300); else this.icueMessage("LED: Plugin not found!"); return; } @@ -495,14 +507,4 @@ AAAASUVORK5CYII= } } - private showWaiting() { - $("#icuetext").html("LED: waiting for plugin."); - $("#icueholder").fadeIn({ queue: false, duration: "fast" }); - $("#icueholder").animate({ top: "0px" }, "fast"); - } - - private hideWaiting() { - $("#icueholder").fadeOut({ queue: false, duration: "fast" }); - $("#icueholder").animate({ top: "-120px" }, "fast"); - } } diff --git a/src/WEWWA.ts b/src/WEWWA.ts index afdbedd..c781dba 100644 --- a/src/WEWWA.ts +++ b/src/WEWWA.ts @@ -45,7 +45,7 @@ * -
document.createElement(e); + const ce = (e) => document.createElement(e); + // local vars faster var proj = this.project; var props = proj.general.properties; // create root menu var menu = ce("div"); - menu.classList.add("wewwaMenu"); + menu.id = "wewwaMenu"; // create preview img wrap var preview = ce("img"); preview.setAttribute("src", proj.preview); @@ -286,9 +293,11 @@ export class WEWWA { // audio input methods var row = ce("tr"); + row.classList.add("show"); + var td1 = ce("td"); td1.innerHTML = "

Audio Input


"; - td1.setAttribute("colspan", 3); + td1.setAttribute("colspan", "3"); var aBtn1 = ce("a"); aBtn1.classList.add("orange"); @@ -310,7 +319,7 @@ export class WEWWA { aBtn3.innerHTML = "Select File"; aBtn3.setAttribute("type", "file"); aBtn3.addEventListener("change", e => { - var file = e.target.files[0]; + var file = (e.target as any).files[0]; if (!file) return; this.initiateAudio(file); }); @@ -320,9 +329,11 @@ export class WEWWA { // file drag & drop area var dropRow = ce("tr"); + dropRow.classList.add("show"); + var dropt1 = ce("td"); var dropt2 = ce("td"); - dropt1.setAttribute("colspan", 3); + dropt1.setAttribute("colspan", "3"); var dropArea = ce("div"); dropArea.innerHTML = "Drag & Drop" dropArea.classList.add(...["droparea", "red"]); @@ -342,6 +353,8 @@ export class WEWWA { // Player & Stop Btn var hrrow = ce("tr"); + hrrow.classList.add("show"); + var hrtd1 = ce("td"); var hrtd2 = ce("td"); var hrstop = ce("a"); @@ -352,7 +365,7 @@ export class WEWWA { }); var hrhr = ce("hr") hrtd1.id = "audioMarker"; - hrtd1.setAttribute("colspan", 3); + hrtd1.setAttribute("colspan", "3"); hrtd1.append(hrstop, hrhr); hrrow.append(hrtd1, hrtd2); @@ -362,6 +375,7 @@ export class WEWWA { // create actual settings wrapper var settings = ce("tr"); + settings.classList.add("show"); settings.innerHTML = "

Settings

"; table.append(settings); @@ -372,6 +386,7 @@ export class WEWWA { if (!lang) lang = DefLang; // add html struct var row = ce("tr"); + row.classList.add("show"); var td1 = ce("td"); td1.innerHTML = "

🇩🇪🇬🇧🇮🇹🇷🇺🇨🇳

"; var td2 = ce("td"); @@ -385,7 +400,7 @@ export class WEWWA { lan.append(lcs); // check for correct language code if (loc != lang) continue; - else lcs.setAttribute("selected", true); + else lcs.setAttribute("selected", "true"); // set properties translated text for (var p in props) { var itm = props[p]; @@ -409,7 +424,7 @@ export class WEWWA { self.EvaluateSettings(); (self.htmlIcon as any).click(); }); - td2.setAttribute("colspan", 2); + td2.setAttribute("colspan", "2"); td2.append(lan); row.append(td1, td2); table.append(row); @@ -417,6 +432,7 @@ export class WEWWA { // split content from actual settings var splitr = ce("tr"); + splitr.classList.add("show"); splitr.innerHTML = "
"; table.append(splitr); @@ -451,9 +467,12 @@ export class WEWWA { // create icon for opening & closing the menu var icon = ce("div"); - icon.classList.add("wewwaIcon"); + icon.id = "wewwaIcon"; icon.addEventListener("click", () => { - $(".wewwaMenu, .wewwaIcon").toggleClass("open"); + if(menu.classList.contains("open")) menu.classList.remove("open"); + else menu.classList.add("open"); + if(icon.classList.contains("open")) icon.classList.remove("open"); + else icon.classList.add("open"); }); var bar1 = ce("div"); var bar2 = ce("div"); @@ -469,7 +488,7 @@ export class WEWWA { private CreateItem(prop, itm) { if (!itm.type || itm.type == "hidden") return; var self = this; - var ce = (e) => document.createElement(e); + const ce = (e) => document.createElement(e); var row = ce("tr"); row.setAttribute("id", "wewwa_" + prop); var td1 = ce("td"); @@ -667,16 +686,15 @@ export class WEWWA { } } - - if (visible) $("#wewwa_" + p).fadeIn(); - else $("#wewwa_" + p).fadeOut(); - // get input dom element + const htElm = document.getElementById("wewwa_" + p); + if (!htElm || htElm.childNodes.length < 2) continue; - var elm: any = document.getElementById("wewwa_" + p); - if (!elm || elm.childNodes.length < 2) continue; + if (visible) htElm.classList.add("show"); + else htElm.classList.remove("show"); - elm = elm.childNodes[1].childNodes[0]; + // set its value + const elm: any = htElm.childNodes[1].childNodes[0]; switch (prop.type) { case "color": elm.value = this.rgbToHex(prop.value); @@ -768,9 +786,11 @@ export class WEWWA { this.audio.src = data.name ? URL.createObjectURL(data) : data; this.audio.autoplay = true; this.audio.setAttribute("controls", "true"); - this.audio.play = true; + this.audio.play(); - $("#audioMarker").prepend(this.audio); + // insert before marker + const markr = document.getElementById("audioMarker"); + markr.parentElement.insertBefore(this.audio, markr); this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); this.source = this.ctx.createMediaElementSource(this.audio); @@ -803,7 +823,7 @@ export class WEWWA { public stopAudioInterval() { window['persistAudioStream'] = null; - $("#wewwaAudioInput").val(""); + document.getElementById("wewwaAudioInput").setAttribute("value", ""); if (this.audio) this.audio.remove(); if (this.audioInterval) { diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 55683cd..dbcb4f1 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -16,7 +16,7 @@ import { CComponent } from "./CComponent"; import { CSettings } from "./CSettings"; -import { Ready } from "./Ready"; +import { Ready } from "./Util"; const ELM_ID = "triggerwarn"; diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 38ec5d0..8e1b807 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -22,7 +22,7 @@ import { ASUtil } from "@assemblyscript/loader"; import { CComponent } from "../CComponent"; import { CSettings } from "../CSettings"; -import { Ready } from "../Ready"; +import { Ready } from "../Util"; import { Smallog } from "../Smallog"; import WascInit from '../wasc-worker'; From aafd7520880a531ed001e9b9c85cf81dc4645be1 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Mon, 5 Apr 2021 18:48:59 +0200 Subject: [PATCH 28/76] Fix Licenses; Full Linting; Add Docs; --- README.md | 1 - Stats.js | 294 +- docs/CComponent.html | 849 +++++ docs/CComponent.ts.html | 184 ++ docs/CSettings.html | 463 +++ docs/CSettings.ts.html | 156 + docs/CUESettings.html | 479 +++ docs/OfflinePlugin.html | 461 +++ docs/ReloadHelper.html | 734 +++++ docs/ReloadHelper.ts.html | 244 ++ docs/ReloadSettings.html | 251 ++ docs/Stats.html | 204 ++ docs/Stats.ts.html | 155 + docs/Util.ts.html | 142 + docs/WEAS.html | 1190 +++++++ docs/WEAS.ts.html | 367 +++ docs/WEASettings.html | 632 ++++ docs/WEICUE.html | 2270 ++++++++++++++ docs/WEICUE.ts.html | 667 ++++ docs/WEWA.ts.html | 1220 ++++++++ docs/WEWWA.html | 2891 ++++++++++++++++++ docs/WarnHelper.html | 926 ++++++ docs/WarnHelper.ts.html | 261 ++ docs/WarnSettings.html | 256 ++ docs/WascBuilderPlugin.html | 435 +++ docs/WascInterface.html | 251 ++ docs/fonts/OpenSans-Bold-webfont.eot | Bin 0 -> 19544 bytes docs/fonts/OpenSans-Bold-webfont.svg | 1830 +++++++++++ docs/fonts/OpenSans-Bold-webfont.woff | Bin 0 -> 22432 bytes docs/fonts/OpenSans-BoldItalic-webfont.eot | Bin 0 -> 20133 bytes docs/fonts/OpenSans-BoldItalic-webfont.svg | 1830 +++++++++++ docs/fonts/OpenSans-BoldItalic-webfont.woff | Bin 0 -> 23048 bytes docs/fonts/OpenSans-Italic-webfont.eot | Bin 0 -> 20265 bytes docs/fonts/OpenSans-Italic-webfont.svg | 1830 +++++++++++ docs/fonts/OpenSans-Italic-webfont.woff | Bin 0 -> 23188 bytes docs/fonts/OpenSans-Light-webfont.eot | Bin 0 -> 19514 bytes docs/fonts/OpenSans-Light-webfont.svg | 1831 +++++++++++ docs/fonts/OpenSans-Light-webfont.woff | Bin 0 -> 22248 bytes docs/fonts/OpenSans-LightItalic-webfont.eot | Bin 0 -> 20535 bytes docs/fonts/OpenSans-LightItalic-webfont.svg | 1835 +++++++++++ docs/fonts/OpenSans-LightItalic-webfont.woff | Bin 0 -> 23400 bytes docs/fonts/OpenSans-Regular-webfont.eot | Bin 0 -> 19836 bytes docs/fonts/OpenSans-Regular-webfont.svg | 1831 +++++++++++ docs/fonts/OpenSans-Regular-webfont.woff | Bin 0 -> 22660 bytes docs/global.html | 1102 +++++++ docs/index.html | 161 + docs/offline_OfflinePlugin.js.html | 266 ++ docs/scripts/app.min.js | 1 + docs/scripts/linenumber.js | 26 + docs/scripts/prettify/Apache-License-2.0.txt | 202 ++ docs/scripts/prettify/lang-css.js | 2 + docs/scripts/prettify/prettify.js | 28 + docs/scripts/search.js | 39 + docs/styles/app.min.css | 1 + docs/styles/iframe.css | 13 + docs/styles/jsdoc-default.css | 358 +++ docs/styles/prettify-jsdoc.css | 111 + docs/styles/prettify-tomorrow.css | 132 + docs/styles/reset.css | 44 + docs/wasc-worker_WascBuilderPlugin.js.html | 322 ++ docs/wasc-worker_WascInterface.ts.html | 136 + docs/wasc-worker_WascLoader.ts.html | 292 ++ docs/wasc-worker_WascRT.ts.html | 277 ++ docs/wasc-worker_WascWorker.ts.html | 147 + docs/wasc-worker_index.ts.html | 147 + jsdoc.json | 25 + makedocs.bat | 3 + src/CComponent.ts | 103 +- src/CSettings.ts | 62 +- src/ReloadHelper.ts | 211 +- src/Smallog.ts | 104 +- src/Stats.ts | 22 +- src/Util.ts | 48 +- src/WEAS.ts | 261 ++ src/{weas => }/WEAS.wasm.asc | 61 +- src/WEICUE.ts | 987 +++--- src/WEWA.ts | 1104 +++++++ src/WEWWA.ts | 834 ----- src/WarnHelper.ts | 250 +- src/offline/Offline.ts | 165 + src/offline/OfflineHelper.ts | 131 +- src/offline/OfflinePlugin.js | 243 +- src/offline/OfflineWorker.ts | 159 - src/wasc-worker | 2 +- src/weas/WEAS.ts | 216 -- src/worker-loader.d.ts | 38 +- 86 files changed, 33418 insertions(+), 2388 deletions(-) create mode 100644 docs/CComponent.html create mode 100644 docs/CComponent.ts.html create mode 100644 docs/CSettings.html create mode 100644 docs/CSettings.ts.html create mode 100644 docs/CUESettings.html create mode 100644 docs/OfflinePlugin.html create mode 100644 docs/ReloadHelper.html create mode 100644 docs/ReloadHelper.ts.html create mode 100644 docs/ReloadSettings.html create mode 100644 docs/Stats.html create mode 100644 docs/Stats.ts.html create mode 100644 docs/Util.ts.html create mode 100644 docs/WEAS.html create mode 100644 docs/WEAS.ts.html create mode 100644 docs/WEASettings.html create mode 100644 docs/WEICUE.html create mode 100644 docs/WEICUE.ts.html create mode 100644 docs/WEWA.ts.html create mode 100644 docs/WEWWA.html create mode 100644 docs/WarnHelper.html create mode 100644 docs/WarnHelper.ts.html create mode 100644 docs/WarnSettings.html create mode 100644 docs/WascBuilderPlugin.html create mode 100644 docs/WascInterface.html create mode 100644 docs/fonts/OpenSans-Bold-webfont.eot create mode 100644 docs/fonts/OpenSans-Bold-webfont.svg create mode 100644 docs/fonts/OpenSans-Bold-webfont.woff create mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.eot create mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.svg create mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.woff create mode 100644 docs/fonts/OpenSans-Italic-webfont.eot create mode 100644 docs/fonts/OpenSans-Italic-webfont.svg create mode 100644 docs/fonts/OpenSans-Italic-webfont.woff create mode 100644 docs/fonts/OpenSans-Light-webfont.eot create mode 100644 docs/fonts/OpenSans-Light-webfont.svg create mode 100644 docs/fonts/OpenSans-Light-webfont.woff create mode 100644 docs/fonts/OpenSans-LightItalic-webfont.eot create mode 100644 docs/fonts/OpenSans-LightItalic-webfont.svg create mode 100644 docs/fonts/OpenSans-LightItalic-webfont.woff create mode 100644 docs/fonts/OpenSans-Regular-webfont.eot create mode 100644 docs/fonts/OpenSans-Regular-webfont.svg create mode 100644 docs/fonts/OpenSans-Regular-webfont.woff create mode 100644 docs/global.html create mode 100644 docs/index.html create mode 100644 docs/offline_OfflinePlugin.js.html create mode 100644 docs/scripts/app.min.js create mode 100644 docs/scripts/linenumber.js create mode 100644 docs/scripts/prettify/Apache-License-2.0.txt create mode 100644 docs/scripts/prettify/lang-css.js create mode 100644 docs/scripts/prettify/prettify.js create mode 100644 docs/scripts/search.js create mode 100644 docs/styles/app.min.css create mode 100644 docs/styles/iframe.css create mode 100644 docs/styles/jsdoc-default.css create mode 100644 docs/styles/prettify-jsdoc.css create mode 100644 docs/styles/prettify-tomorrow.css create mode 100644 docs/styles/reset.css create mode 100644 docs/wasc-worker_WascBuilderPlugin.js.html create mode 100644 docs/wasc-worker_WascInterface.ts.html create mode 100644 docs/wasc-worker_WascLoader.ts.html create mode 100644 docs/wasc-worker_WascRT.ts.html create mode 100644 docs/wasc-worker_WascWorker.ts.html create mode 100644 docs/wasc-worker_index.ts.html create mode 100644 jsdoc.json create mode 100644 makedocs.bat create mode 100644 src/WEAS.ts rename src/{weas => }/WEAS.wasm.asc (85%) create mode 100644 src/WEWA.ts delete mode 100644 src/WEWWA.ts create mode 100644 src/offline/Offline.ts delete mode 100644 src/offline/OfflineWorker.ts delete mode 100644 src/weas/WEAS.ts diff --git a/README.md b/README.md index adcdcae..ebd68f8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ Keeping track of this stuff manually is annoying... ### Dependencies / Libraries - [typescript](https://www.typescriptlang.org/) for typization -- [jQuery](https://jquery.com/) gui editing - [three.js](https://threejs.org/) & Examples for webgl rendering diff --git a/Stats.js b/Stats.js index 4bd78fd..bcddea8 100644 --- a/Stats.js +++ b/Stats.js @@ -1,151 +1,147 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global.Stats = factory()); -}(this, (function () { - 'use strict'; - - /** +(function(global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.Stats = factory()); +}(this, (function() { + 'use strict'; + + /** * @author mrdoob / http://mrdoob.com/ + * @return {Object} stats */ - - var Stats = function () { - - var mode = 0; - - var container = document.createElement('div'); - container.style.cssText = 'position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:90000'; - container.addEventListener('click', function (event) { - - event.preventDefault(); - showPanel(++mode % container.children.length); - - }, false); - - function addPanel(panel) { - container.appendChild(panel.dom); - return panel; - } - - function showPanel(id) { - for (var i = 0; i < container.children.length; i++) { - container.children[i].style.display = i === id ? 'block' : 'none'; - } - mode = id; - } - - function dispose() { - for (var i = 0; i < container.children.length; i++) { - container.removeChild(container.children[i]); - } - container.parentNode.removeChild(container); - } - - var beginTime = (performance || Date).now(), prevTime = beginTime, frames = 0; - - var fpsPanel = addPanel(new Stats.Panel('FPS', '#0ff', '#002')); - var msPanel = addPanel(new Stats.Panel('MS', '#0f0', '#020')); - - if (self.performance && self.performance.memory) { - var memPanel = addPanel(new Stats.Panel('MB', '#f08', '#201')); - } - - showPanel(0); - - return { - REVISION: 16, - - dom: container, - - addPanel: addPanel, - showPanel: showPanel, - dispose: dispose, - // Backwards Compatibility - domElement: container, - setMode: showPanel, - - begin: function () { - beginTime = (performance || Date).now(); - }, - end: function () { - - frames++; - var time = (performance || Date).now(); - msPanel.update(time - beginTime, 200); - - if (time >= prevTime + 1000) { - - fpsPanel.update((frames * 1000) / (time - prevTime), 100); - prevTime = time; - frames = 0; - - if (memPanel) { - var memory = performance.memory; - memPanel.update(memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576); - } - } - return time; - }, - update: function () { - beginTime = this.end(); - } - }; - }; - - Stats.Panel = function (name, fg, bg) { - var min = Infinity, max = 0, round = Math.round; - var PR = round(window.devicePixelRatio || 1); - - var WIDTH = 80 * PR, HEIGHT = 48 * PR, - TEXT_X = 3 * PR, TEXT_Y = 2 * PR, - GRAPH_X = 3 * PR, GRAPH_Y = 15 * PR, - GRAPH_WIDTH = 74 * PR, GRAPH_HEIGHT = 30 * PR; - - var canvas = document.createElement('canvas'); - canvas.width = WIDTH; - canvas.height = HEIGHT; - canvas.style.cssText = 'width:80px;height:48px'; - - var context = canvas.getContext('2d'); - context.font = 'bold ' + (9 * PR) + 'px Helvetica,Arial,sans-serif'; - context.textBaseline = 'top'; - - context.fillStyle = bg; - context.fillRect(0, 0, WIDTH, HEIGHT); - - context.fillStyle = fg; - context.fillText(name, TEXT_X, TEXT_Y); - context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT); - - context.fillStyle = bg; - context.globalAlpha = 0.9; - context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT); - - return { - - dom: canvas, - - update: function (value, maxValue) { - - min = Math.min(min, value); - max = Math.max(max, value); - - context.fillStyle = bg; - context.globalAlpha = 1; - context.fillRect(0, 0, WIDTH, GRAPH_Y); - context.fillStyle = fg; - context.fillText(round(value) + ' ' + name + ' (' + round(min) + '-' + round(max) + ')', TEXT_X, TEXT_Y); - - context.drawImage(canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT); - - context.fillRect(GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT); - - context.fillStyle = bg; - context.globalAlpha = 0.9; - context.fillRect(GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round((1 - (value / maxValue)) * GRAPH_HEIGHT)); - } - }; - }; - - return Stats; -}))); \ No newline at end of file + /* eslint-disable require-jsdoc */ + const Stats = function() { + let mode = 0; + + const container = document.createElement('div'); + container.style.cssText = 'position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:90000'; + container.addEventListener('click', function(event) { + event.preventDefault(); + showPanel(++mode % container.children.length); + }, false); + + function addPanel(panel) { + container.appendChild(panel.dom); + return panel; + } + + function showPanel(id) { + for (let i = 0; i < container.children.length; i++) { + container.children[i].style.display = i === id ? 'block' : 'none'; + } + mode = id; + } + + function dispose() { + for (let i = 0; i < container.children.length; i++) { + container.removeChild(container.children[i]); + } + container.parentNode.removeChild(container); + } + + let beginTime = (performance || Date).now(); let prevTime = beginTime; let frames = 0; + + const fpsPanel = addPanel(new Stats.Panel('FPS', '#0ff', '#002')); + const msPanel = addPanel(new Stats.Panel('MS', '#0f0', '#020')); + let memPanel; + + if (self.performance && self.performance.memory) { + memPanel = addPanel(new Stats.Panel('MB', '#f08', '#201')); + } + + showPanel(0); + + return { + REVISION: 16, + + dom: container, + + addPanel: addPanel, + showPanel: showPanel, + dispose: dispose, + // Backwards Compatibility + domElement: container, + setMode: showPanel, + + begin: function() { + beginTime = (performance || Date).now(); + }, + end: function() { + frames++; + const time = (performance || Date).now(); + msPanel.update(time - beginTime, 200); + + if (time >= prevTime + 1000) { + fpsPanel.update((frames * 1000) / (time - prevTime), 100); + prevTime = time; + frames = 0; + + if (memPanel) { + const memory = performance.memory; + memPanel.update(memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576); + } + } + return time; + }, + update: function() { + beginTime = this.end(); + }, + }; + }; + + Stats.Panel = function(name, fg, bg) { + let min = Infinity; let max = 0; const round = Math.round; + const PR = round(window.devicePixelRatio || 1); + + const WIDTH = 80 * PR; const HEIGHT = 48 * PR; + const TEXT_X = 3 * PR; const TEXT_Y = 2 * PR; + const GRAPH_X = 3 * PR; const GRAPH_Y = 15 * PR; + const GRAPH_WIDTH = 74 * PR; const GRAPH_HEIGHT = 30 * PR; + + const canvas = document.createElement('canvas'); + canvas.width = WIDTH; + canvas.height = HEIGHT; + canvas.style.cssText = 'width:80px;height:48px'; + + const context = canvas.getContext('2d'); + context.font = 'bold ' + (9 * PR) + 'px Helvetica,Arial,sans-serif'; + context.textBaseline = 'top'; + + context.fillStyle = bg; + context.fillRect(0, 0, WIDTH, HEIGHT); + + context.fillStyle = fg; + context.fillText(name, TEXT_X, TEXT_Y); + context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT); + + context.fillStyle = bg; + context.globalAlpha = 0.9; + context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT); + + return { + + dom: canvas, + + update: function(value, maxValue) { + min = Math.min(min, value); + max = Math.max(max, value); + + context.fillStyle = bg; + context.globalAlpha = 1; + context.fillRect(0, 0, WIDTH, GRAPH_Y); + context.fillStyle = fg; + context.fillText(round(value) + ' ' + name + ' (' + round(min) + '-' + round(max) + ')', TEXT_X, TEXT_Y); + + context.drawImage(canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT); + + context.fillRect(GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT); + + context.fillStyle = bg; + context.globalAlpha = 0.9; + context.fillRect(GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round((1 - (value / maxValue)) * GRAPH_HEIGHT)); + }, + }; + }; + + return Stats; +}))); diff --git a/docs/CComponent.html b/docs/CComponent.html new file mode 100644 index 0000000..3873b4a --- /dev/null +++ b/docs/CComponent.html @@ -0,0 +1,849 @@ + + + + + + + + CComponent + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

CComponent

+
+ + + + + +
+ +
+ +

CComponent()

+ +
Base-Component for a TypeScript Web Wallpaper
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new CComponent() + + +

+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + CComponent.ts, line 6 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + +

Classes

+ +
+
CComponent
+
+
+ + + + + + + + + +
+

Members

+
+ +
+ +

+ # + + + settings + + +

+ + + + +
+ main Settings, need to be overwritten with Specific settings +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
See:
+
+ +
+ + + + + +

+ View Source + + CComponent.ts, line 12 + +

+ +
+ + + + + +
+ +
+ + + + +CSettings + + + + +

+ # + + + settings + + +

+ + + + +
+ main Settings, need to be overwritten with Specific settings +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
See:
+
+ +
+ + + + + +

+ View Source + + CComponent.ts, line 66 + +

+ +
+ + + + + +
+ +
+
+ + + +
+

Methods

+
+ +
+ + + +

+ # + + + + applySetting(key, value) → {boolean} + + +

+ + + + +
+ will recursively try to set a setting with type and return success
will also flag the module as needs-Update. +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +Object + + + +
value + + +Object + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + CComponent.ts, line 25 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
found
+ + +
+ + +boolean + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + updateAll() + + +

+ + + + +
+ DO NOT OVERWWRITE !!!
will recursively update all needed modules after settings changes +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + CComponent.ts, line 39 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + updateSettings() → {Promise} + + +

+ + + + +
+ NEEDS TO BE OVERWRITTEN FOR DOING ACTIONS ON SETTINGS CHANGE.
should usually get called automatically when needed.. no need for extra calling +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + CComponent.ts, line 52 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
async commpletion event
+ + +
+ + +Promise + + +
+ +
+ + +
+
+ + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/CComponent.ts.html b/docs/CComponent.ts.html new file mode 100644 index 0000000..4078bd3 --- /dev/null +++ b/docs/CComponent.ts.html @@ -0,0 +1,184 @@ + + + + + + + + + + CComponent.ts + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

CComponent.ts

+
+ + + + + +
+
+
/**
+* @author hexxone / https://hexx.one
+*
+* @license
+* Copyright (c) 2021 hexxone All rights reserved.
+* Licensed under the GNU GENERAL PUBLIC LICENSE.
+* See LICENSE file in the project root for full license information.
+*/
+
+import {CSettings} from './CSettings';
+import {Smallog} from './Smallog';
+
+/**
+* Base-Component for a TypeScript Web Wallpaper
+*/
+export class CComponent {
+	private needsUpdate = false;
+
+	/** main Settings, need to be overwritten with Specific settings
+	 * @see {CSettings}
+	 */
+	public settings: CSettings = null;
+
+	/* Important: Append your child objects, for settings to be applied correctly! */
+	public children: CComponent[] = [];
+
+	/**
+	* will recursively try to set a setting with type and return success
+	* <br/>
+	* will also flag the module as needs-Update.
+	*
+	* @param {Object} key
+	* @param {Object} value
+	* @return {boolean} found
+	*/
+	public applySetting(key: any, value: any): boolean {
+		let found = this.settings.apply(key, value);
+		if (found) {
+			this.needsUpdate = true;
+			Smallog.debug(`ApplySetting: ${key}:${value}`);
+		}
+		this.children.forEach((ch) => found = (found || ch.applySetting(key, value)));
+		return found;
+	}
+
+	/**
+	* DO NOT OVERWWRITE !!!
+	* <br/>
+	* will recursively update all needed modules after settings changes
+	*/
+	public updateAll() {
+		this.children.forEach((c) => c.updateAll());
+		if (this.needsUpdate) this.updateSettings();
+		this.needsUpdate = false;
+	}
+
+	/**
+	* NEEDS TO BE OVERWRITTEN FOR DOING ACTIONS ON SETTINGS CHANGE.
+	* <br/>
+	* should usually get called automatically when needed.. no need for extra calling
+	*
+	* @return {Promise} async commpletion event
+	*/
+	public updateSettings(): Promise<void> {
+		console.error(`ERROR_NO_IMPL at: CComponent.UpdateSettings!\r\nPlease override this method!`);
+		return Promise.resolve();
+	}
+}
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/CSettings.html b/docs/CSettings.html new file mode 100644 index 0000000..867087d --- /dev/null +++ b/docs/CSettings.html @@ -0,0 +1,463 @@ + + + + + + + + CSettings + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

CSettings

+
+ + + + + +
+ +
+ +

CSettings()

+ +
Base-Component Settings helper Core Settings interface, used for type-secure setting applying. All Settings-classes should be dereived from this one.
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new CSettings() + + +

+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
See:
+
+ +
+ + + + + +

+ View Source + + CSettings.ts, line 20 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + + + + + + + + + + + +
+

Methods

+
+ +
+ + + +

+ # + + + + apply(key, castedValue) → {boolean} + + +

+ + + + +
+ check if a certain key exists on a (dereived) object and the value type matches +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +string + + + +
castedValue + + +Object + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + CSettings.ts, line 27 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
success
+ + +
+ + +boolean + + +
+ +
+ + +
+
+ + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/CSettings.ts.html b/docs/CSettings.ts.html new file mode 100644 index 0000000..802abd4 --- /dev/null +++ b/docs/CSettings.ts.html @@ -0,0 +1,156 @@ + + + + + + + + + + CSettings.ts + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

CSettings.ts

+
+ + + + + +
+
+
/**
+* @author hexxone / https://hexx.one
+*
+* @license
+* Copyright (c) 2021 hexxone All rights reserved.
+* Licensed under the GNU GENERAL PUBLIC LICENSE.
+* See LICENSE file in the project root for full license information.
+*/
+
+import {Smallog} from './Smallog';
+
+/**
+* Base-Component Settings helper
+*
+* Core Settings interface, used for type-secure setting applying.
+*
+* All Settings-classes should be dereived from this one.
+*
+* @see {CComponent}
+*/
+export class CSettings {
+	/**
+	* check if a certain key exists on a (dereived) object and the value type matches
+	* @param {string} key
+	* @param {Object} castedValue
+	* @return {boolean} success
+	*/
+	public apply(key: string, castedValue: any) {
+		if (this[key] !== undefined) {
+			if (typeof this[key] === typeof castedValue) {
+				this[key] = castedValue;
+				return true;
+			} else {
+				Smallog.Error('CSettings Error: invalid type on: \'' + key +
+				'\'. Is: \'' + typeof this[key] + '\', applied: \'' + typeof castedValue + '\'');
+			}
+		}
+		return false;
+	}
+}
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/CUESettings.html b/docs/CUESettings.html new file mode 100644 index 0000000..12a3afd --- /dev/null +++ b/docs/CUESettings.html @@ -0,0 +1,479 @@ + + + + + + + + CUESettings + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

CUESettings

+
+ + + + + +
+ +
+ +

CUESettings()

+ +
iCUE processing settings
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new CUESettings() + + +

+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 23 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Extends

+ + + + + + + + + + + + +

Classes

+ +
+
CUESettings
+
+
+ + + + + + + + + + + +
+

Methods

+
+ +
+ + + +

+ # + + + + apply(key, castedValue) → {boolean} + + +

+ + + + +
+ check if a certain key exists on a (dereived) object and the value type matches +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +string + + + +
castedValue + + +Object + + + +
+
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + CSettings.ts, line 27 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
success
+ + +
+ + +boolean + + +
+ +
+ + +
+
+ + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/OfflinePlugin.html b/docs/OfflinePlugin.html new file mode 100644 index 0000000..78d5b61 --- /dev/null +++ b/docs/OfflinePlugin.html @@ -0,0 +1,461 @@ + + + + + + + + OfflinePlugin + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

OfflinePlugin

+
+ + + + + +
+ +
+ +

OfflinePlugin(options)

+ +
This is a webpack plugin for easily making your webapp available Offline.
It will simply output a json list of files to cache for the Service Worker on build.
In your source, you just 'require' and 'Register()' the OfflineHelper.
The OfflineHelper will then require the OfflineWorker in background.

The OfflineWorker will then:
1. launch itself
2. load the 'offlinefiles.json' list
3. cache all files in it
4. return cached files if network fetching fails

You can also ignore this plugin and create the 'offlinefiles.json' yourself.
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new OfflinePlugin(options) + + +

+ + + + +
+ Intializes the plugin in the webpack build process +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
options + + +offliineSchema + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + offline/OfflinePlugin.js, line 87 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + + + + + + + + + + + +
+

Methods

+
+ +
+ + + +

+ # + + + + apply(compiler) + + +

+ + + + +
+ Hook into the compilation process, find all target files and put them into a json file +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
compiler + + +Webpack.compiler + + + + object from webpack
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + offline/OfflinePlugin.js, line 104 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/ReloadHelper.html b/docs/ReloadHelper.html new file mode 100644 index 0000000..5ee78b8 --- /dev/null +++ b/docs/ReloadHelper.html @@ -0,0 +1,734 @@ + + + + + + + + ReloadHelper + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

ReloadHelper

+
+ + + + + +
+ +
+ +

ReloadHelper()

+ +
Visual Reload-Bar
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new ReloadHelper() + + +

+ + + + +
+ Create and prepare when document is ready +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + ReloadHelper.ts, line 29 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + +

Classes

+ +
+
ReloadHelper
+
+
+ + + + + + + + + + + +
+

Methods

+
+ +
+ + + +

+ # + + + + injectCSS() + + +

+ + + + +
+ Make custom style +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + ReloadHelper.ts, line 44 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + injectHTML() + + +

+ + + + +
+ Make custom html elements +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + ReloadHelper.ts, line 91 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + show(visible) + + +

+ + + + + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
visible + + +boolean + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
To Do:
+
+
    +
  • test: bar always reset to 0 on show ??
  • +
+
+ + + +

+ View Source + + ReloadHelper.ts, line 106 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + updateSettings() → {Promise} + + +

+ + + + +
+ dont print IMPL error +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + ReloadHelper.ts, line 122 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ + +
+ + +Promise + + +
+ +
+ + +
+
+ + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/ReloadHelper.ts.html b/docs/ReloadHelper.ts.html new file mode 100644 index 0000000..ef9d0f5 --- /dev/null +++ b/docs/ReloadHelper.ts.html @@ -0,0 +1,244 @@ + + + + + + + + + + ReloadHelper.ts + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

ReloadHelper.ts

+
+ + + + + +
+
+
/**
+* @author hexxone / https://hexx.one
+*
+* @license
+* Copyright (c) 2021 hexxone All rights reserved.
+* Licensed under the GNU GENERAL PUBLIC LICENSE.
+* See LICENSE file in the project root for full license information.
+*
+* @description
+* Displays a html reload bar for a given Time.
+*
+*/
+
+import {CComponent} from './CComponent';
+import {CSettings} from './CSettings';
+import {waitReady} from './Util';
+
+/**
+ * Reload-bar settings
+ */
+class ReloadSettings extends CSettings {
+	reload_seconds: number = 3;
+}
+
+/**
+ * Visual Reload-Bar
+ */
+export class ReloadHelper extends CComponent {
+	public settings: ReloadSettings = new ReloadSettings();
+
+	/**
+	 * Create and prepare when document is ready
+	 */
+	constructor() {
+		super();
+		waitReady().then(() => {
+			this.injectCSS();
+			this.injectHTML();
+		});
+	}
+
+	/**
+	 * Make custom style
+	 */
+	private injectCSS() {
+		const st = document.createElement('style');
+		st.innerHTML = `
+		#reload-bar {
+			position: absolute;
+			opacity: 0;
+			top: 0px;
+			height: 10px;
+			width: 0%;
+			background-color: #989a;
+		}
+		#reload-bar.show {
+			opacity: 1;
+			width: 100%;
+			background-color: #e11a;
+			transition: all ${this.settings.reload_seconds}s ease, opacity 0.33s ease;
+		}
+		#reload-bar.done {
+			transition: opacity 0.33s ease;
+		}
+		#reload-text {
+			position: absolute;
+			top: -6em;
+			width: 100%;
+			text-align: center;
+			font-weight: 100;
+			font-size: 3em;
+			color: #fffa;
+		}
+		#reload-text.show {
+			top: 10px;
+			color: #e11a;
+			text-shadow: 0 0 20px rgba(255, 50, 50, .5), 0 0 15px rgba(255, 50, 50, .5);
+			transition: all .33s ease, color ${this.settings.reload_seconds}s ease, text-shadow ${this.settings.reload_seconds}s ease;
+		}
+		#reload-text.done {
+			transition: position 0.33s linear;
+		}
+		#reload-text {
+			text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5);
+		}
+		`;
+		document.head.append(st);
+	}
+
+	/**
+	 * Make custom html elements
+	 */
+	private injectHTML() {
+		const outer = document.createElement('div');
+		outer.id = 'reloadhelper';
+		const bar = document.createElement('div');
+		bar.id = 'reload-bar';
+		const tex = document.createElement('h1');
+		tex.id = 'reload-text';
+		tex.innerHTML = 'Reload';
+		outer.append(bar, tex);
+		document.body.append(outer);
+	}
+
+	/**
+	 * @Todo test: bar always reset to 0 on show ??
+	 * @param {boolean} visible
+	 */
+	public show(visible: boolean) {
+		const e1 = document.getElementById('reload-bar');
+		const e2 = document.getElementById('reload-text');
+		if (visible) {
+			e1.classList.add('show');
+			e2.classList.add('show');
+		} else {
+			e1.classList.remove('show');
+			e2.classList.remove('show');
+		}
+	}
+
+	/**
+	 * dont print IMPL error
+	 * @return {Promise}
+	 */
+	public updateSettings(): Promise<void> {
+		return Promise.resolve();
+	}
+};
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/ReloadSettings.html b/docs/ReloadSettings.html new file mode 100644 index 0000000..d03a85a --- /dev/null +++ b/docs/ReloadSettings.html @@ -0,0 +1,251 @@ + + + + + + + + ReloadSettings + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

ReloadSettings

+
+ + + + + +
+ +
+ +

ReloadSettings()

+ +
Reload-bar settings
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new ReloadSettings() + + +

+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + ReloadHelper.ts, line 20 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/Stats.html b/docs/Stats.html new file mode 100644 index 0000000..047a8a0 --- /dev/null +++ b/docs/Stats.html @@ -0,0 +1,204 @@ + + + + + + + + Stats + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Interface

+

Stats

+
+ + + + + +
+ +
+ +

Stats

+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + +
Author:
+
+
    +
  • hexxone / https://hexx.one
  • +
+
+ + + + + +
License:
+
  • Copyright (c) 2021 hexxone All rights reserved. Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
+ + + + + + + + + + +

+ View Source + + Stats.ts, line 4 + +

+ +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/Stats.ts.html b/docs/Stats.ts.html new file mode 100644 index 0000000..86a8abb --- /dev/null +++ b/docs/Stats.ts.html @@ -0,0 +1,155 @@ + + + + + + + + + + Stats.ts + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

Stats.ts

+
+ + + + + +
+
+
/**
+* @author hexxone / https://hexx.one
+*
+* @license
+* Copyright (c) 2021 hexxone All rights reserved.
+* Licensed under the GNU GENERAL PUBLIC LICENSE.
+* See LICENSE file in the project root for full license information.
+*/
+
+/**
+* @ignore
+* TypeScript Wrapper for mrdoob Stats.js
+* Still requires Stats.js to be included!
+*/
+declare interface Stats {
+	REVISION: number;
+	dom: HTMLDivElement;
+	addPanel(panel: Stats.Panel): Stats.Panel;
+	showPanel(id: number): void;
+	begin(): void;
+	end(): void;
+	update(): void;
+	domElement: HTMLDivElement;
+	setMode(id: number): void;
+}
+
+declare function Stats(): Stats;
+
+declare namespace Stats {
+	interface Panel {
+		dom: HTMLCanvasElement;
+		update(value: number, maxValue: number): void;
+	}
+
+	// eslint-disable-next-line no-unused-vars
+	function Panel(name?: string, fg?: string, bg?: string): Panel;
+}
+
+export default Stats;
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/Util.ts.html b/docs/Util.ts.html new file mode 100644 index 0000000..2e2cf26 --- /dev/null +++ b/docs/Util.ts.html @@ -0,0 +1,142 @@ + + + + + + + + + + Util.ts + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

Util.ts

+
+ + + + + +
+
+
/**
+* @author hexxone / https://hexx.one
+*
+* @license
+* Copyright (c) 2021 hexxone All rights reserved.
+* Licensed under the GNU GENERAL PUBLIC LICENSE.
+* See LICENSE file in the project root for full license information.
+*
+* @description
+* Shorthand Document ready wrapper
+*/
+
+/**
+* Helper function
+* @return {Promise} resolve when site ready
+*/
+export function waitReady() {
+	return new Promise((resolve) => {
+		// If document is already loaded, run method
+		if (document.readyState === 'interactive' || document.readyState === 'complete') {
+			resolve(true);
+		}
+		// Otherwise, wait until document is loaded
+		document.addEventListener('DOMContentLoaded', resolve, false);
+	});
+}
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/WEAS.html b/docs/WEAS.html new file mode 100644 index 0000000..55b32b6 --- /dev/null +++ b/docs/WEAS.html @@ -0,0 +1,1190 @@ + + + + + + + + WEAS + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

WEAS

+
+ + + + + +
+ +
+ +

WEAS()

+ +
WEAS
Wallpaper Engine Audio Supplier makes working with audio easier.
It will automatically start to receive and process the audio data which can then be accessed on the global object.
DEPENDS ON:
- Wallpaper Engine Web Wallpaper environment
- audio-processing supported web wallpaper
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new WEAS() + + +

+ + + + +
+ delay audio initialization until page ready +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEAS.ts, line 80 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Extends

+ + + + + + + + + + + + +

Classes

+ +
+
WEAS
+
+
+ + + + + + + + + +
+

Members

+
+ +
+ +

+ # + + + settings + + +

+ + + + +
+ main Settings, need to be overwritten with Specific settings +
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + + +
See:
+
+ +
+ + + + + +

+ View Source + + CComponent.ts, line 12 + +

+ +
+ + + + + +
+ +
+
+ + + +
+

Methods

+
+ +
+ + + +

+ # + + + + applySetting(key, value) → {boolean} + + +

+ + + + +
+ will recursively try to set a setting with type and return success
will also flag the module as needs-Update. +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +Object + + + +
value + + +Object + + + +
+
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + CComponent.ts, line 25 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
found
+ + +
+ + +boolean + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + getProps(dProps) → {Object} + + +

+ + + + +
+ converts calculated output property number-array to string-associative-array +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
dProps + + +ArrayLike.<number> + + + + processed properties
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEAS.ts, line 163 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ + +
+ + +Object + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + hasAudio() → {boolean} + + +

+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEAS.ts, line 218 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
false if:
- processing is disabled
- there is no data
- the data is silent
- data is too old (> 3s)
+ + +
+ + +boolean + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + async + + + + + realInit() + + +

+ + + + +
+ initializes audio processing pipeline and starts listening on audio data +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEAS.ts, line 100 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + updateAll() + + +

+ + + + +
+ DO NOT OVERWWRITE !!!
will recursively update all needed modules after settings changes +
+ + + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + CComponent.ts, line 39 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + updateSettings() → {Promise} + + +

+ + + + +
+ !! CAVEAT: only available after init and module load !!
Will send the processing settings to the WebAssembly module +
+ + + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEAS.ts, line 178 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
finished event
+ + +
+ + +Promise + + +
+ +
+ + +
+
+ + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/WEAS.ts.html b/docs/WEAS.ts.html new file mode 100644 index 0000000..472a4f8 --- /dev/null +++ b/docs/WEAS.ts.html @@ -0,0 +1,367 @@ + + + + + + + + + + WEAS.ts + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

WEAS.ts

+
+ + + + + +
+
+
/**
+* @author hexxone / https://hexx.one
+*
+* @license
+* Copyright (c) 2021 hexxone All rights reserved.
+* Licensed under the GNU GENERAL PUBLIC LICENSE.
+* See LICENSE file in the project root for full license information.
+*/
+
+import {ASUtil} from '@assemblyscript/loader';
+import {CComponent} from './CComponent';
+import {CSettings} from './CSettings';
+import {waitReady} from './Util';
+import {Smallog} from './Smallog';
+
+import wascWorker from './wasc-worker/WascWorker';
+import {WascInterface} from './wasc-worker/WascInterface';
+
+const DAT_LEN = 128;
+
+/**
+* Audio processing settings
+* @extends {CSettings}
+*/
+export class WEASettings extends CSettings {
+	/** do audio processing? */
+	public audioprocessing: boolean = true;
+	// do pink-noise processing?
+	public equalize: boolean = true;
+	// convert to mono?
+	public mono_audio: boolean = true;
+	// invert low & high freqs?
+	public audio_direction: number = 0;
+	// peak filtering
+	public peak_filter: number = 1;
+	// neighbour-smoothing value
+	public value_smoothing: number = 2;
+	// time-value smoothing ratio
+	public audio_increase: number = 75;
+	public audio_decrease: number = 35;
+	// multipliers
+	public treble_multiplier: number = 0.5;
+	public mids_multiplier: number = 0.75;
+	public bass_multiplier: number = 1.8;
+	// ignore value leveling for "silent" data
+	public minimum_volume: number = 0.005;
+	// use low latency audio?
+	public low_latency: boolean = false;
+}
+
+const SettIDs = {
+	equalize: 0,
+	mono_audio: 1,
+	audio_direction: 2,
+	peak_filter: 3,
+	value_smoothing: 4,
+	audio_increase: 5,
+	audio_decrease: 6,
+	treble_multiplier: 7,
+	mids_multiplier: 8,
+	bass_multiplier: 9,
+	minimum_volume: 10,
+};
+
+const PropIDs = {
+	bass: 0,
+	mids: 1,
+	highs: 2,
+	sum: 3,
+	min: 4,
+	max: 5,
+	average: 6,
+	range: 7,
+	silent: 8,
+	intensity: 9,
+};
+
+/**
+* WEAS
+* <br/>
+* Wallpaper Engine Audio Supplier makes working with audio easier.
+* <br/>
+* It will automatically start to receive and process the audio data
+* which can then be accessed on the global object.
+* <br/>
+* DEPENDS ON:
+* <br/>
+* - Wallpaper Engine Web Wallpaper environment
+* <br/>
+* - audio-processing supported web wallpaper
+* @extends {CComponent}
+*/
+export class WEAS extends CComponent {
+	// last processed audio object
+	public lastAudio = null;
+
+	// settings object
+	public settings: WEASettings = new WEASettings();
+
+	// create transfer buffer
+	private inBuff = new Float64Array(DAT_LEN);
+
+	// web assembly functions
+	private weasModule: WascInterface = null;
+
+	/**
+	* delay audio initialization until page ready
+	*/
+	constructor() {
+		super();
+		waitReady().then(() => this.realInit());
+	}
+
+	/**
+	* initializes audio processing pipeline
+	* and starts listening on audio data
+	*/
+	private async realInit() {
+		// if wallpaper engine context given, listen
+		if (!window['wallpaperRegisterAudioListener']) {
+			Smallog.info('\'window.wallpaperRegisterAudioListener\' not given!');
+			return;
+		}
+
+		this.weasModule = await wascWorker('WEAS.wasm', {}, !this.settings.low_latency);
+		const {run} = this.weasModule;
+
+		// pass settings to module
+		await this.updateSettings();
+
+		const self = this;
+
+		// register audio callback on module
+		window['wallpaperRegisterAudioListener']((audioArray) => {
+			// check proof
+			if (!audioArray || !self.settings.audioprocessing) return;
+			if (audioArray.length != DAT_LEN) {
+				Smallog.Error('audioListener: received invalid audio data array. Length: ' + audioArray.length);
+				return;
+			}
+
+			// prepare data
+			const start = performance.now();
+			self.inBuff.set(audioArray);
+
+			// WRAP IN isolated Function ran inside worker
+			run(({module, instance, importObject, params}) => {
+				const {exports} = instance;
+				const {data} = params[0];
+				const io = importObject as ASUtil;
+
+				// set audio data directly in module memory
+				io.__getFloat64ArrayView(exports.inputData).set(data);
+				// trigger processing processing
+				exports.update();
+				// get copy of updated Data & Properties
+				const r = { // => result
+					data: new Float64Array(io.__getFloat64ArrayView(exports.outputData)),
+					props: new Float64Array(io.__getFloat64ArrayView(exports.audioProps)),
+				};
+				console.log(r);
+				return r;
+			}, // params passed to worker
+			{
+				data: self.inBuff,
+			})
+			// worker result, back in main context
+				.then((result) => {
+					const {data, props} = result;
+					const realProps = self.getProps(props);
+					// apply actual last Data from worker
+					self.lastAudio = {
+						time: start,
+						data,
+						...realProps,
+					};
+					// print info
+					Smallog.info('Got Data from Worker! Time= ' + (performance.now() - start) + ', Data= ' + JSON.stringify(realProps));
+				});
+		});
+	}
+
+	/**
+	* converts calculated output property number-array to string-associative-array
+	* @param {ArrayLike<number>} dProps processed properties
+	* @return {Object}
+	*/
+	private getProps(dProps: ArrayLike<number>) {
+		const keys = Object.keys(PropIDs);
+		const res = {};
+		for (let index = 0; index < keys.length; index++) {
+			const key = keys[index];
+			res[key] = dProps[PropIDs[key]];
+		}
+		return res;
+	}
+
+	/**
+	* !! CAVEAT: only available after init and module load !!
+	* <br/>
+	* Will send the processing settings to the WebAssembly module
+	* @return {Promise} finished event
+	*/
+	public updateSettings(): Promise<void> {
+		if (!this.weasModule) return;
+		const {run} = this.weasModule;
+
+		return new Promise((resolve) => {
+			const keys = Object.keys(SettIDs);
+			const sett = new Float64Array(keys.length);
+			for (let index = 0; index < keys.length; index++) {
+				const key = keys[index];
+				sett[SettIDs[key]] = this.settings[key] || 0;
+			}
+
+			// isolated Function running inside worker
+			run(({module, instance, importObject, params}) => {
+				const {exports} = instance;
+				const {data} = params[0];
+				const io = importObject as ASUtil;
+				io.__getFloat64ArrayView(exports.audioSettings).set(data);
+			},
+			// Data passed to worker
+			{
+				data: sett,
+			})
+			// Back to main context
+				.then(() => {
+					Smallog.info('Sent Settings to WEAS: ' + JSON.stringify(sett));
+					resolve();
+				});
+		});
+	}
+
+	/**
+	* @return {boolean} false if:
+	* <br/>
+	* - processing is disabled
+	* <br/>
+	* - there is no data
+	* <br/>
+	* - the data is silent
+	* <br/>
+	* - data is too old (> 3s)
+	*/
+	public hasAudio() {
+		return this.settings.audioprocessing &&
+		this.lastAudio && this.lastAudio.silent == 0 &&
+		(performance.now() / 1000 - this.lastAudio.time < 3);
+	}
+}
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/WEASettings.html b/docs/WEASettings.html new file mode 100644 index 0000000..76c34ff --- /dev/null +++ b/docs/WEASettings.html @@ -0,0 +1,632 @@ + + + + + + + + WEASettings + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

WEASettings

+
+ + + + + +
+ +
+ +

WEASettings()

+ +
Audio processing settings
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new WEASettings() + + +

+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEAS.ts, line 12 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Extends

+ + + + + + + + + + + + +

Classes

+ +
+
WEASettings
+
+
+ + + + + + + + + +
+

Members

+
+ +
+ +

+ # + + + audioprocessing + + +

+ + + + +
+ do audio processing? +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEAS.ts, line 16 + +

+ +
+ + + + + +
+ +
+ + + + +boolean + + + + +

+ # + + + audioprocessing + + +

+ + + + +
+ do audio processing? +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEAS.ts, line 238 + +

+ +
+ + + + + +
+ +
+
+ + + +
+

Methods

+
+ +
+ + + +

+ # + + + + apply(key, castedValue) → {boolean} + + +

+ + + + +
+ check if a certain key exists on a (dereived) object and the value type matches +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +string + + + +
castedValue + + +Object + + + +
+
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + CSettings.ts, line 27 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
success
+ + +
+ + +boolean + + +
+ +
+ + +
+
+ + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/WEICUE.html b/docs/WEICUE.html new file mode 100644 index 0000000..19d0f17 --- /dev/null +++ b/docs/WEICUE.html @@ -0,0 +1,2270 @@ + + + + + + + + WEICUE + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

WEICUE

+
+ + + + + +
+ +
+ +

WEICUE(weas)

+ +
WEICUE
Wallpaper Engine iCUE effects for web wallpapers
Uses several different methods to create Lighting effects for Corsair ICUE devices.
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new WEICUE(weas) + + +

+ + + + +
+ Starts listening for led/icue plugin and prepares helper elements +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
weas + + +WEAS + + + + Audio supplier for non-projection mode
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 48 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Extends

+ + + + + + + + + + + + +

Classes

+ +
+
WEICUE
+
+
+ + + + + + + + + +
+

Members

+
+ +
+ +

+ # + + + settings + + +

+ + + + +
+ main Settings, need to be overwritten with Specific settings +
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + + +
See:
+
+ +
+ + + + + +

+ View Source + + CComponent.ts, line 12 + +

+ +
+ + + + + +
+ +
+
+ + + +
+

Methods

+
+ +
+ + + +

+ # + + + + applySetting(key, value) → {boolean} + + +

+ + + + +
+ will recursively try to set a setting with type and return success
will also flag the module as needs-Update. +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +Object + + + +
value + + +Object + + + +
+
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + CComponent.ts, line 25 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
found
+ + +
+ + +boolean + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + gBlurCanvas(canvas, ctx, blur) + + +

+ + + + +
+ canvas blur helper function +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
canvas + + +HTMLCanvasElement + + + +
ctx + + +CanvasRenderingContext2D + + + +
blur + + +number + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 375 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + getArea(inPx) → {Object} + + +

+ + + + +
+ helper +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
inPx + + +boolean + + + + + + false + + suffix "px" string to number (allows direct css use)
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 339 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
area
+ + +
+ + +Object + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + getEncodedCanvasImageData(imageData) → {string} + + +

+ + + + +
+ convert data for icue +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
imageData + + +ImageData + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 359 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ + +
+ + +string + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + icueMessage(msg, waiting) + + +

+ + + + +
+ show a message by icue +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
msg + + +string + + + + + +
waiting + + +boolean + + + + + + false + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 318 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + init() + + +

+ + + + +
+ Show waiting message and init canvas +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 399 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + initCUE(count) + + +

+ + + + +
+ will initialize ICUE api & usage +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
count + + +number + + + + Retries (will stop at 100)
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 450 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + injectCSS() + + +

+ + + + +
+ style for iCue messages, preview and helper +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 89 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + injectHTML() + + +

+ + + + +
+ Prepare html elements +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 133 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + updateAll() + + +

+ + + + +
+ DO NOT OVERWWRITE !!!
will recursively update all needed modules after settings changes +
+ + + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + CComponent.ts, line 39 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + updateCanvas(mainCanvas) + + +

+ + + + +
+ copy main canvas portion to our helper +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
mainCanvas + + +HTMLCanvasElementq + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 521 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + updateFrame() + + +

+ + + + +
+ do the thing... +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 478 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + updateSettings() → {Promise} + + +

+ + + + +
+ show or hide preview +
+ + + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEICUE.ts, line 427 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
finished
+ + +
+ + +Promise + + +
+ +
+ + +
+
+ + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/WEICUE.ts.html b/docs/WEICUE.ts.html new file mode 100644 index 0000000..64fb329 --- /dev/null +++ b/docs/WEICUE.ts.html @@ -0,0 +1,667 @@ + + + + + + + + + + WEICUE.ts + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

WEICUE.ts

+
+ + + + + +
+
+
/**
+* @author hexxone / https://hexx.one
+*
+* @license
+* Copyright (c) 2021 hexxone All rights reserved.
+* Licensed under the GNU GENERAL PUBLIC LICENSE.
+* See LICENSE file in the project root for full license information.
+*/
+
+import {CComponent} from './CComponent';
+import {CSettings} from './CSettings';
+import {waitReady} from './Util';
+import {Smallog} from './Smallog';
+import {WEAS} from './WEAS';
+
+const ClassName: string = '[WEICUE] ';
+const canvasX: number = 23;
+const canvasY: number = 7;
+const WaitTime: number = 30;
+const Transition: number = 3;
+
+/**
+* iCUE processing settings
+* @extends {CSettings}
+*/
+export class CUESettings extends CSettings {
+	public icue_mode: number = 1;
+	public icue_area_xoff: number = 50;
+	public icue_area_yoff: number = 90;
+	public icue_area_width: number = 75;
+	public icue_area_height: number = 30;
+	public icue_area_blur: number = 5;
+	public icue_area_decay: number = 15;
+	public icue_area_preview: boolean = false;
+	public icue_main_color: string = '0 0.8 0';
+	// AudiOrbits bg Color; used as "decay"-color aswell
+	public main_color: string = '0 0 0';
+}
+
+/**
+* WEICUE
+* <br/>
+* Wallpaper Engine iCUE effects for web wallpapers
+* <br/>
+* Uses several different methods to create
+* Lighting effects for Corsair ICUE devices.
+* @extends {CComponent}
+*/
+export class WEICUE extends CComponent {
+	private weas: WEAS = null;
+
+	private holder: HTMLDivElement = null;
+	private texter: HTMLDivElement = null;
+	private preview: HTMLDivElement = null;
+	private helperCanvas: HTMLCanvasElement = null;
+	private helperContext: CanvasRenderingContext2D = null;
+
+	private icueDevices = [];
+	private icueInterval = null;
+
+	// runtime values
+	public settings: CUESettings = new CUESettings();
+
+	public isAvailable: boolean = false;
+	public PAUSED: boolean = false;
+
+	/**
+	* Starts listening for led/icue plugin
+	* and prepares helper elements
+	* @param {WEAS} weas Audio supplier for non-projection mode
+	*/
+	constructor(weas: WEAS) {
+		super();
+		this.weas = weas;
+
+		// Plugin handler
+		window['wallpaperPluginListener'] = {
+			onPluginLoaded: function(name, version) {
+				Smallog.info('Plugin: ' + name + ', Version: ' + version + ' : registered.', ClassName);
+				if (name === 'cue') this.isAvailable = true;
+				if (name === 'led') this.isAvailable = true;
+			},
+		};
+		waitReady().then(() => {
+			// inject helpers
+			this.injectCSS();
+			this.injectHTML();
+			this.init();
+		});
+	}
+
+	/**
+	* style for iCue messages, preview and helper
+	*/
+	private injectCSS() {
+		const st = document.createElement('style');
+		st.innerHTML = `
+		#icueholder {
+			opacity: 0;
+			position: absolute;
+			top: -120px;
+			left: 0;
+			width: auto;
+			height: auto;
+			margin: 10px;
+			transition: all ${Transition}s ease;
+		}
+		#icueholder.show {
+			opacity: 1;
+			top: 0px;
+		}
+		#icueholder.waiting {
+			opacity: 0.2;
+			transition: all 10s ease;
+		}
+		#icuelogo {
+			float: left;
+			height: 80px;
+			width: 80px;
+		}
+		#icuetext {
+			float: left;
+			margin: 25px 5px;
+			font-size: 175%;
+		}
+		#icueholder {
+			text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5);
+		}
+		.cuePreview {
+			position: absolute;
+			background: rgba(255, 0, 0, .3);
+		}
+		`;
+		document.head.append(st);
+	}
+
+	/**
+	* Prepare html elements
+	*/
+	private injectHTML() {
+		// create container
+		this.holder = document.createElement('div');
+		this.holder.id = 'icueholder';
+		// create icon (no ref needed)
+		const imgg = document.createElement('img');
+		imgg.id = 'icuelogo';
+		// @TODO remove this abomination
+		imgg.setAttribute('src', `
+		data:image/png;base64,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
+		AAAASUVORK5CYII=
+		`);
+		// make text holder
+		this.texter = document.createElement('div');
+		this.texter.id = 'icuetext';
+		// append image and text
+		this.holder.append(imgg, this.texter);
+		document.body.append(this.holder);
+	}
+
+	/**
+	* show a message by icue
+	* @param {string} msg
+	* @param {boolean} waiting
+	*/
+	private icueMessage(msg: string, waiting :boolean = false) {
+		Smallog.debug('MSG:  ' + msg, ClassName);
+		// set text
+		this.texter.innerHTML = msg;
+		// show
+		this.holder.classList.add('show');
+		if (waiting) this.holder.classList.add('waiting');
+		// hide again
+		const waiTime = waiting ? (WaitTime) : (Transition * 1000 + 4000);
+		setTimeout(() => {
+			this.holder.classList.remove('show');
+			if (waiting) this.holder.classList.remove('waiting');
+		}, waiTime);
+	}
+
+	/**
+	* helper
+	* @param {boolean} inPx suffix "px" string to number (allows direct css use)
+	* @return {Object} area
+	*/
+	private getArea(inPx = false) {
+		const sett = this.settings;
+		const wwid = window.innerWidth;
+		const whei = window.innerHeight;
+		const w = wwid * sett.icue_area_width / 100;
+		const h = whei * sett.icue_area_height / 100;
+		const l = ((wwid - w) * sett.icue_area_xoff / 100);
+		const t = ((whei - h) * sett.icue_area_yoff / 100);
+		return {
+			width: w + (inPx ? 'px' : ''),
+			height: h + (inPx ? 'px' : ''),
+			left: l + (inPx ? 'px' : ''),
+			top: t + (inPx ? 'px' : ''),
+		};
+	}
+
+	/**
+	* convert data for icue
+	* @param {ImageData} imageData
+	* @return {string}
+	*/
+	private getEncodedCanvasImageData(imageData: ImageData) {
+		const colorArray = [];
+		for (let d = 0; d < imageData.data.length; d += 4) {
+			const write = d / 4 * 3;
+			colorArray[write] = imageData.data[d];
+			colorArray[write + 1] = imageData.data[d + 1];
+			colorArray[write + 2] = imageData.data[d + 2];
+		}
+		return String.fromCharCode.apply(null, colorArray);
+	}
+
+	/**
+	* canvas blur helper function
+	* @param {HTMLCanvasElement} canvas
+	* @param {CanvasRenderingContext2D} ctx
+	* @param {number} blur
+	*/
+	private gBlurCanvas(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, blur: number) {
+		let sum = 0;
+		const delta = 5;
+		const alpha_left = 1 / (2 * Math.PI * delta * delta);
+		const step = blur < 3 ? 1 : 2;
+
+		let x; let weight;
+		for (let y = -blur; y <= blur; y += step) {
+			for (x = -blur; x <= blur; x += step) {
+				weight = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta));
+				sum += weight;
+			}
+		}
+		for (let y = -blur; y <= blur; y += step) {
+			for (x = -blur; x <= blur; x += step) {
+				ctx.globalAlpha = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta)) / sum * blur * blur;
+				ctx.drawImage(canvas, x, y);
+			}
+		}
+		ctx.globalAlpha = 1;
+	}
+
+	/**
+	* Show waiting message and init canvas
+	*/
+	private init() {
+		const sett = this.settings;
+		// dont initialize if disabled
+		if (sett.icue_mode == 0) return;
+
+		this.icueMessage('LED: waiting for plugin.', true);
+		this.initCUE(0);
+		Smallog.debug('init...', ClassName);
+
+		// recreate if reinit
+		if (this.icueInterval) clearInterval(this.icueInterval);
+		if (this.helperCanvas) document.body.removeChild(this.helperCanvas);
+		// setup canvas
+		this.helperCanvas = document.createElement('canvas');
+		this.helperCanvas.id = 'helpCvs';
+		this.helperCanvas.width = canvasX;
+		this.helperCanvas.height = canvasY;
+		this.helperCanvas.style.display = 'none';
+		this.helperContext = this.helperCanvas.getContext('2d');
+		document.body.appendChild(this.helperCanvas);
+
+		// update devices about every 33ms/30fps. iCue doesnt really support higher values
+		this.icueInterval = window.setInterval(() => this.updateFrame(), 1000 / 30);
+	}
+
+
+	/**
+	* show or hide preview
+	* @return {Promise} finished
+	*/
+	public updateSettings(): Promise<void> {
+		const sett = this.settings;
+		// create preview?
+		if (!this.preview && sett.icue_area_preview) {
+			this.preview = document.createElement('div');
+			this.preview.classList.add('cuePreview');
+			document.body.appendChild(this.preview);
+		}
+		// update settings or destroy
+		if (this.preview) {
+			if (!sett.icue_area_preview) {
+				document.body.removeChild(this.preview);
+				this.preview = null;
+			} else Object.assign(this.preview.style, this.getArea(true));
+		}
+		return Promise.resolve();
+	}
+
+	/**
+	* will initialize ICUE api & usage
+	* @param {number} count Retries (will stop at 100)
+	*/
+	private initCUE(count) {
+		// wait for plugins
+		if (!this.isAvailable) {
+			if (count < 100) setTimeout(() => this.initCUE(++count), 300);
+			else this.icueMessage('LED: Plugin not found!');
+			return;
+		}
+		// setup devices
+		this.icueDevices = [];
+
+		window['cue'].getDeviceCount((deviceCount) => {
+			this.icueMessage('LED: Found ' + deviceCount + ' devices.');
+			for (let xi = 0; xi < deviceCount; xi++) {
+				const xl = xi;
+				window['cue'].getDeviceInfo(xl, (info) => {
+					info.id = xl;
+					window['cue'].getLedPositionsByDeviceIndex(xl, (leds) => {
+						info.leds = leds;
+						this.icueDevices[xl] = info;
+					});
+				});
+			}
+		});
+	}
+
+	/**
+	*  do the thing...
+	*/
+	private updateFrame() {
+		const sett = this.settings;
+		if (this.PAUSED || !this.isAvailable || sett.icue_mode == 0 || this.icueDevices.length < 1) return;
+		// projection mode
+		if (sett.icue_mode == 1) {
+			// get scaled down image data and encode it for icue
+			const encDat = this.getEncodedCanvasImageData(this.helperContext.getImageData(0, 0, canvasX, canvasY));
+			// update all icueDevices with data
+			for (let xi = 0; xi < this.icueDevices.length; xi++) {
+				window['cue'].setLedColorsByImageData(xi, encDat, canvasX, canvasY);
+			}
+		}
+		// color mode
+		if (sett.icue_mode == 2) {
+			// get lol objects
+			const col = sett.icue_main_color.split(' ') as unknown[];
+			let ledColor = {
+				r: col[0] as number * 255,
+				g: col[1] as number * 255,
+				b: col[2] as number * 255,
+			}; ;
+			// try audio multiplier processing
+			if (this.weas.hasAudio()) {
+				const aud = this.weas.lastAudio;
+				const mlt = 255 * aud.average / aud.range / aud.intensity * 10;
+				ledColor = {
+					r: Math.min(255, Math.max(0, col[0] as number * mlt)),
+					g: Math.min(255, Math.max(0, col[1] as number * mlt)),
+					b: Math.min(255, Math.max(0, col[2] as number * mlt)),
+				};
+			}
+			// update all icueDevices with data
+			for (let xi = 0; xi < this.icueDevices.length; xi++) {
+				window['cue'].setAllLedsColorsAsync(xi, ledColor);
+			}
+		}
+	}
+
+	/**
+	 * copy main canvas portion to our helper
+	 * @param {HTMLCanvasElementq} mainCanvas
+	 */
+	public updateCanvas(mainCanvas: HTMLCanvasElement) {
+		const sett = this.settings;
+		if (!this.isAvailable || !mainCanvas || sett.icue_mode == 0 || this.icueDevices.length < 1) return;
+
+		if (sett.icue_mode == 1) {
+			// get helper vars
+			const area: any = this.getArea(false);
+			const hctx = this.helperContext;
+			// get real rgb values
+			const spl = sett.main_color.split(' ') as unknown[];
+			for (let i = 0; i < spl.length; i++) spl[i] = (spl[i] as number * 255);
+			// overlay "decay" style
+			hctx.fillStyle = 'rgba(' + spl.join(', ') + ', ' + sett.icue_area_decay / 100 + ')';
+			hctx.fillRect(0, 0, canvasX, canvasY);
+			// scale down and copy the image to the helper canvas
+			hctx.drawImage(mainCanvas, area.left, area.top, area.width, area.height, 0, 0, canvasX, canvasY);
+			// blur the helper projection canvas
+			if (sett.icue_area_blur > 0) this.gBlurCanvas(this.helperCanvas, hctx, sett.icue_area_blur);
+		}
+	}
+}
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/WEWA.ts.html b/docs/WEWA.ts.html new file mode 100644 index 0000000..077724a --- /dev/null +++ b/docs/WEWA.ts.html @@ -0,0 +1,1220 @@ + + + + + + + + + + WEWA.ts + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

WEWA.ts

+
+ + + + + +
+
+
/**
+* @author hexxone / https://hexx.one
+*
+* @license
+* Copyright (c) 2021 hexxone All rights reserved.
+* Licensed under the GNU GENERAL PUBLIC LICENSE.
+* See LICENSE file in the project root for full license information.
+*/
+
+import {waitReady} from './Util';
+import {Smallog} from './Smallog';
+import {OfflineHelper} from './offline/OfflineHelper';
+import {CC} from 'cookieconsent';
+import {myFetch} from './wasc-worker/WascRT';
+
+const LogHead = '[WEWWA] ';
+const DefLang = 'de-de';
+
+/**
+* WEWWA
+* <br/>
+* Wallpaper Engine Web Wallpaper Adapter
+* <br/>
+* This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine
+* Web-Wallpaper project - so you can test, run & configure it from a normal web browser.
+* <br/>
+* REQUIREMENTS:
+* <br/>
+* - HTML5 Browser
+* <br/>
+* - the "project.json" needs to be in the root folder like "index.html"
+* <br/>
+* - this file needs to be included/built in your "index.html"
+* <br/>
+* <br/>
+* FEATURES:
+* <br/>
+* - automatically detecting if the web wallpaper is opened by wallpaper engine or browser
+* <br/>
+* - if opened by wallpaper engine, nothing will happen
+* <br/>
+* - if opened by a browser:
+* <br/>
+*   - use a ServiceWorker to make page always available offline
+* <br/>
+*   - automatically load the "project.json"
+* <br/>
+*   - parse the settings, languages & conditions
+* <br/>
+*   - add respective html elements for each setting type & condition
+* <br/>
+*   - put these elements into an option menu which can be hidden
+* <br/>
+*   - check localStorage for already saved/customized values
+* <br/>
+*   - apply all settings once
+* <br/>
+* - react to changes made in the ui and update them in the wallpaper
+* <br/>
+* - save changes made in the ui to localStorage
+* <br/>
+* - Annoying Cookie Popup (Thanks DSGVO)
+*
+*
+* @todo
+* - inject "audio processing" setting
+*
+* lighthouse:
+* - image explicit width/height
+* - cf longer cache policy (2d?)
+* - <img alt's
+* - <form <input <label's
+ */
+export class WEWWA {
+	private project: any = null;
+
+	private htmlMenu: Element = null;
+	private htmlIcon: Element = null;
+
+	private audio: HTMLAudioElement = null;
+	private ctx: any = null;
+	private source: any = null;
+	private analyser: any = null;
+
+	private audioInterval: any = null;
+	private audioCallback: any = null;
+
+	private pauseOnUnfocus: boolean = true;
+	private isPaused: boolean = false;
+
+	/**
+	 * Check if we are running in Web-Mode
+	 * if yes => iniitialize, else => do nothing
+	 * @param {Function} finished Callback
+	 */
+	constructor(finished) {
+		if (window['wallpaperRegisterAudioListener']) {
+			Smallog.info('detected wallpaper engine => Standby.', LogHead);
+			finished();
+			return;
+		}
+
+		Smallog.info('wallpaper engine not detected => Init!', LogHead);
+
+		// define audio listener first, so we dont miss when it gets registered.
+		window['wallpaperRegisterAudioListener'] = (callback) => {
+			// set callback to be called later with analysed audio data
+			this.audioCallback = callback;
+			Smallog.info('Registered wallpaper AudioListener.', LogHead);
+		};
+
+		// intialize when ready
+		waitReady().then(() => {
+			if (CC) {/* This tells the compiler to include CookieConsent at this point. */}
+
+			// Thanks DSGVO...
+			window['cookieconsent'].initialise({
+				palette: {
+					popup: {background: '#000'},
+					button: {background: '#f1d600'},
+				},
+				position: 'bottom-left',
+				theme: 'edgeless',
+			});
+
+			// make the website available offline using service worker
+			OfflineHelper.register(document.title.replace(' ', '')).then(() => {
+				// continue initializing
+				finished();
+				this.init();
+
+				// pause and resume on focus events
+				window.onblur = () => this.setPaused(true);
+				window.onfocus = () => this.setPaused(false);
+			});
+		});
+	}
+
+	/**
+	 * Initialize the Web Adapter
+	 */
+	private init() {
+		myFetch('project.json', 'json').then((proj) => {
+			if (proj.type != 'web') {
+				Smallog.Error('Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...', LogHead);
+				return;
+			}
+			this.project = proj;
+			this.loadStorage();
+			this.addStyle();
+			this.addMenu(localStorage.getItem('wewwaLang'));
+			this.evaluateSettings();
+			this.applyProp(proj.general.properties);
+		});
+	}
+
+	/**
+	 * Load last settings from localStorage
+	 */
+	private loadStorage() {
+		const props = this.project.general.properties;
+		const last = localStorage.getItem('wewwaLastProps');
+		if (last != null) {
+			const merged = Object.assign(props, JSON.parse(last));
+			merged.audioprocessing = {
+				value: this.project.general.supportsaudioprocessing,
+				type: 'hidden',
+			};
+			this.project.general.properties = merged;
+			Smallog.debug('Loaded & merged settings.', LogHead);
+		}
+	}
+
+	/**
+	 * CSS Insertion
+	 */
+	private addStyle() {
+		const st = document.createElement('style');
+		// precalculation
+		const minWidthPx = 420;
+		const percentageWidth = 20;
+		const pwShort = `${percentageWidth}vw`;
+		st.innerHTML = `
+		#wewwaMenu, #wewwaIcon {
+			transform: none;
+			transition: transform 500ms ease;
+			position:absolute;
+			top:0px;
+			padding:15px;
+			z-index:9999;
+		}
+		#wewwaMenu {
+			top:10px;
+			border: solid 2px #444;
+			width:${pwShort};
+			left:100vw;
+			color:white;
+			background-color: rgba(0.6,0.6,0.6,0.8);
+			overflow-x:hidden;
+			overflow-y:scroll;
+			max-height:92.5%;
+			min-width: ${minWidthPx}px;
+			max-width: 100vw;
+			font-family: Helvetica, Verdana, Arial;
+			font-size: larger;
+		}
+		#wewwaMenu hr {
+			margin: 20px 0px;
+		}
+		#wewwaMenu a {
+			color: white;
+			border: 2px solid #4CAF50;
+			padding: 5px 10px;
+			margin: 5px;
+			text-decoration: none;
+			display: block;
+		}
+		#wewwaMenu a:hover {
+			background: #4CAF50;
+		}
+		#wewwaMenu .red {
+			border-color: #FF7F50;
+		}
+		#wewwaMenu .red:hover {
+			background-color: #FF7F50;
+		}
+		#wewwaMenu .audio {
+			border-color: #00a1ff;
+		}
+		#wewwaMenu .audio:hover {
+			background-color: #00a1ff;
+		}
+		#wewwaMenu audio, #wewwaMenu select {
+			width: 100%;
+		}
+		#wewwaMenu table {
+			width:100%;
+			table-layout: fixed;
+		}
+		#wewwaMenu tr.hide {
+			display: none;
+		}
+		#wewwaMenu td {
+			width: 50%;
+			padding: 5px;
+		}
+		#wewwaMenu .left {
+			text-align: left;
+		}
+		#wewwaMenu .right {
+			text-align: right;
+		}
+		#wewwaMenu img {
+			width: ${percentageWidth / 2}vw;
+			min-width: ${Math.floor(minWidthPx / 2)}px;
+			max-width: 90%;
+			heigth: auto;
+		}
+		#wewwaMenu .droparea {
+			border: 2px dashed #bbb;
+			-webkit-border-radius: 5px;
+			border-radius: 5px;
+			padding: 20px;
+			text-align: center;
+			font: 18pt;
+			color: #bbb;
+		}
+		/* Icon */
+		#wewwaIcon {
+			right:0px;
+			cursor:pointer;
+		}
+		#wewwaIcon div {
+			width:35px;
+			height:5px;
+			background-color:#888888;
+			margin:6px 0;
+		}
+		
+		#wewwaMenu.open, #wewwaIcon.open {
+			transform: translateX(min(-${percentageWidth * 1.1}vw, -${Math.floor(minWidthPx * 1.1)}px));
+			transition: transform 500ms ease;
+		}
+		
+		/* Smartphone format */
+		@media all and (max-width: 1000px) {
+			#wewwaMenu {
+				width:90vw;
+			}
+			#wewwaMenu.open {
+				transform: translateX(-95vw);
+				transition: transform 500ms ease;
+			}
+			#wewwaIcon.open {
+				transform: translateX(calc(-100vw + 60px));
+				transition: transform 500ms ease;
+			}
+		}
+		`;
+		document.head.append(st);
+	}
+
+	/**
+	* HTML Creation
+	* @param {string} lang WE language
+	*/
+	private addMenu(lang) {
+		const self = this;
+		if (this.htmlMenu) {
+			document.body.removeChild(this.htmlMenu);
+			document.body.removeChild(this.htmlIcon);
+			this.htmlMenu = null;
+		}
+
+		// quick wrapper, we need this a lot
+		const ce = (e) => document.createElement(e);
+
+		// local vars faster
+		const proj = this.project;
+		const props = proj.general.properties;
+
+		// create root menu
+		this.htmlMenu = ce('div');
+		this.htmlMenu.id = 'wewwaMenu';
+
+		// create preview img wrap
+		this.addMenuHeader(ce, proj);
+		// create table with settings
+		this.addMenuSettings(ce, proj, self, lang, props);
+		// Add Footer
+		this.addMenuFooter(ce);
+		// finally add the menu to the DOM
+		document.body.append(this.htmlMenu);
+
+		// last create the icon for opening & closing the menu
+		this.addMenuIcon(ce);
+	}
+
+	/**
+	* Adds the Menu Icon
+	* @param {Function} ce CreateElement
+	* @param {Element} menu
+	*/
+	private addMenuIcon(ce: (e: any) => any, menu = this.htmlMenu) {
+		const icon = this.htmlIcon = ce('div');
+		icon.id = 'wewwaIcon';
+		icon.addEventListener('click', () => {
+			if (this.htmlMenu.classList.contains('open')) {
+				this.htmlMenu.classList.remove('open');
+			} else {
+				this.htmlMenu.classList.add('open');
+			}
+			if (icon.classList.contains('open')) {
+				icon.classList.remove('open');
+			} else {
+				icon.classList.add('open');
+			}
+		});
+		const bar1 = ce('div');
+		const bar2 = ce('div');
+		const bar3 = ce('div');
+		icon.append(bar1, bar2, bar3);
+		document.body.append(icon);
+	}
+
+	/**
+	 * Adds the actual Wallpaper Props as HTML
+	 * @param {Function} ce Create Element wrapper
+	 * @param {Object} proj project
+	 * @param {object} self this
+	 * @param {string} lang
+	 * @param {object} props
+	 * @param {Element} menu
+	 */
+	private addMenuSettings(ce: (e: any) => any, proj: any, self: this, lang: string, props: any, menu = this.htmlMenu) {
+		const tbl = ce('table');
+		tbl.innerHTML = '<col style="width:50%"> <col style="width:30%"> <col style="width:20%">';
+		const tblBody = ce('tbody');
+		tbl.append(tblBody);
+
+		// if app supports audio, add input menu & handlers
+		if (proj.general.supportsaudioprocessing) {
+			this.addMenuAudio(ce, tblBody);
+		}
+
+		// create actual settings wrapper
+		const settings = ce('tr');
+		settings.innerHTML = '<td colspan=3><hr><h2>Settings</h2><hr></td>';
+		tblBody.append(settings);
+
+		// pause checkbox
+		const pauseRow = ce('tr');
+		const pauseOne = ce('td');
+		pauseOne.innerHTML = '<h4>Pause on Unfocus</h4>';
+		const pauseTwo = ce('td');
+		pauseTwo.setAttribute('colspan', '2');
+		const pauseBox = ce('input');
+		pauseBox.setAttribute('type', 'checkbox');
+		pauseBox.setAttribute('checked', this.pauseOnUnfocus);
+		pauseBox.addEventListener('change', function(e) {
+			// eslint-disable-next-line no-invalid-this
+			self.pauseOnUnfocus = this.checked;
+			// unpause if paused
+			if (!self.pauseOnUnfocus && self.isPaused) {
+				self.setPaused(false);
+			}
+		});
+		pauseTwo.append(pauseBox);
+		pauseRow.append(pauseOne, pauseTwo);
+		tblBody.append(pauseRow);
+
+		// language select?
+		const local = proj.general.localization;
+		if (local) {
+			// set default language
+			if (!lang) {
+				lang = DefLang;
+			}
+			// add default strings
+			this.mergeLocals(local);
+			// add language menu row
+			const row = this.makeMenuLocalization(ce, lang, local, props);
+			tblBody.append(row);
+		}
+
+		// split content from actual settings
+		const splitr = ce('tr');
+		splitr.innerHTML = '<td colspan=3><hr></td>';
+		tblBody.append(splitr);
+
+		// sort settings by order
+		const sortable = [];
+		for (const p in props) {
+			if (p) sortable.push([p, props[p]]);
+		}
+		sortable.sort((a, b) => a[1].order - b[1].order);
+		// add setting html elements
+		for (const s of sortable) {
+			const itm = this.createItem(s[0], s[1]);
+			if (itm) tblBody.append(itm);
+		}
+
+		// pre-footer for resetting saved settings
+		// finish up menu
+		menu.append(tbl);
+	}
+
+	/**
+	 * Add missing default localization strings
+	 * @param {Object} local
+	 */
+	private mergeLocals(local: any) {
+		const locDefs = {
+			'ui_browse_properties_scheme_color': 'Scheme color',
+		};
+		for (const loc in local) {
+			if (!local[loc]) continue;
+			for (const def in locDefs) {
+				if (!local[loc][def]) {
+					local[loc][def] = locDefs[def];
+				}
+			}
+		}
+	}
+
+	/**
+	 * Adds the Footer Link to the Menu
+	 * @param {Function} ce create element
+	 * @param {Element} menu
+	 */
+	private addMenuFooter(ce: (e: any) => any, menu = this.htmlMenu) {
+		const preFoot = ce('div');
+		preFoot.innerHTML = '<hr>';
+
+		const reset = ce('a');
+		reset.classList.add('red');
+		reset.innerHTML = 'Reset ↩️';
+		reset.addEventListener('click', (e) => {
+			if (!window.confirm('This action will clear ALL local data!\r\n\r\nAre you sure?')) {
+				return;
+			}
+			OfflineHelper.reset().then(() => {
+				localStorage.clear();
+				location = location;
+			});
+		});
+		preFoot.append(reset);
+
+		// footer with ident
+		const footer = ce('div');
+		footer.innerHTML = `
+		<hr>
+		<p style='text-align:left; width:11ch; margin:auto; padding:auto;'>
+		[W]allpaper<br>
+		[E]ngine<br>
+		[W]eb<br>
+		[A]dapter
+		</p>
+		<a rel=\"noreferrer\" target=\"_blank\" href=\"https://hexx.one\">by hexxone</a>
+		`;
+
+		menu.append(preFoot, footer);
+	}
+
+	// eslint-disable-next-line valid-jsdoc
+	/**
+	 * Add Language Menu
+	 */
+	private makeMenuLocalization(ce: (e: any) => any, lang, local, props) {
+		const self = this;
+		// add html struct
+		const row = ce('tr');
+		const td1 = ce('td');
+		td1.innerHTML = '<h1>🌍</h1>';
+		const td2 = ce('td');
+		const lan = ce('select');
+		// process all
+		for (const loc in local) {
+			if (!loc) continue;
+			// build select option for this
+			const lcs = ce('option');
+			lcs.value = loc;
+			lcs.innerHTML = loc.toUpperCase();
+			lan.append(lcs);
+			// check for correct language code
+			if (loc != lang) continue;
+			else lcs.setAttribute('selected', 'true');
+			// set properties translated text
+			for (const p in props) {
+				if (!p) continue;
+				const itm = props[p];
+				const pTxt = itm.text;
+				const rTxt = local[loc][pTxt];
+				if (rTxt) itm.realText = rTxt;
+				// process combo box values
+				if (itm.type == 'combo') {
+					for (const o of itm.options) {
+						const lTxt = local[loc][o.label];
+						if (lTxt) o.realLabel = lTxt;
+					}
+				}
+			}
+		}
+		// if changed, do it all over again.
+		lan.addEventListener('change', function(e) {
+			// eslint-disable-next-line no-invalid-this
+			localStorage.setItem('wewwaLang', this.value);
+			// eslint-disable-next-line no-invalid-this
+			self.addMenu(this.value);
+			self.evaluateSettings();
+			(self.htmlIcon as any).click();
+		});
+		td2.setAttribute('colspan', '2');
+		td2.append(lan);
+		row.append(td1, td2);
+		return row;
+	}
+
+	// eslint-disable-next-line valid-jsdoc
+	/**
+	 * Add Audio Menu
+	 */
+	private addMenuAudio(ce: (e: any) => any, tblBody: any) {
+		// audio input methods
+		const row = ce('tr');
+
+		const td1 = ce('td');
+		td1.innerHTML = '<hr><h2>Audio Input</h2><hr>';
+		td1.setAttribute('colspan', '3');
+
+		// Microphone input
+		const aBtn1 = ce('a');
+		aBtn1.classList.add('audio');
+		aBtn1.innerHTML = 'Microphone';
+		aBtn1.addEventListener('click', (e) => {
+			this.requestMicrophone();
+		});
+
+		// File Url input
+		const aBtn2 = ce('a');
+		aBtn2.classList.add('audio');
+		aBtn2.innerHTML = 'Select URL';
+		aBtn2.addEventListener('click', (e) => {
+			const uri = prompt('Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!', 'https://example.com/test.mp3');
+			this.initiateAudio(uri);
+		});
+
+		// System file input
+		const aBtn3 = ce('input');
+		aBtn3.id = 'wewwaAudioInput';
+		aBtn3.innerHTML = 'Select File';
+		aBtn3.setAttribute('type', 'file');
+		aBtn3.addEventListener('change', (e) => {
+			const file = (e.target as any).files[0];
+			if (!file) {
+				return;
+			}
+			this.initiateAudio(file);
+		});
+
+		td1.append(aBtn1, aBtn2, aBtn3);
+		row.append(td1);
+
+
+		// file drag & drop area
+		const dropRow = ce('tr');
+		const dropCol1 = ce('td');
+		const dropCol2 = ce('td');
+		dropCol1.setAttribute('colspan', '3');
+
+		const dropArea = ce('div');
+		dropArea.innerHTML = 'Drag & Drop';
+		dropArea.classList.add(...['droparea', 'audio']);
+		dropArea.addEventListener('dragover', (evt) => {
+			evt.stopPropagation();
+			evt.preventDefault();
+			evt.dataTransfer.dropEffect = 'copy';
+		}, false);
+		dropArea.addEventListener('drop', (e) => {
+			e.stopPropagation();
+			e.preventDefault();
+			const droppedFiles = e.dataTransfer.files;
+			this.initiateAudio(droppedFiles[0]);
+		}, false);
+		dropCol1.append(dropArea);
+		dropRow.append(dropCol1, dropCol2);
+
+
+		// Play & Stop Btn
+		const hrrow = ce('tr');
+		const hrtd1 = ce('td');
+		hrtd1.id = 'audioMarker';
+		hrtd1.setAttribute('colspan', '3');
+		const stopBtn = ce('a');
+		stopBtn.classList.add('red');
+		stopBtn.innerHTML = 'Stop All Audio';
+		stopBtn.addEventListener('click', (e) => {
+			this.stopAudioInterval();
+		});
+		hrtd1.append(stopBtn);
+		const hrtd2 = ce('td');
+		hrrow.append(hrtd1, hrtd2);
+
+		// finally add rows to table
+		tblBody.append(row, dropRow, hrrow);
+	}
+
+	// eslint-disable-next-line valid-jsdoc
+	/**
+	 * Add preview Image, Title and Link
+	 */
+	private addMenuHeader(ce: (e: any) => any, proj: any, menu = this.htmlMenu) {
+		const preview = ce('img');
+		preview.setAttribute('src', proj.preview);
+		// create menu app title
+		const header = ce('div');
+		header.innerHTML = '<h2>' + proj.title + '</h2>';
+		// create workshop link
+		const link = ce('a');
+		link.setAttribute('rel', 'noreferrer');
+		link.setAttribute('href', 'https://steamcommunity.com/sharedfiles/filedetails/?id=' + proj.workshopid);
+		link.setAttribute('target', '_blank');
+		link.innerHTML = '<h3>Open Workshop Page</h3>';
+		menu.append(preview, header, link);
+	}
+
+	// eslint-disable-next-line valid-jsdoc
+	/**
+	 * Create an HTML Menu Item from project json property
+	 */
+	private createItem(prop, itm) {
+		if (!itm.type || itm.type == 'hidden') return null;
+		const self = this;
+		const ce = (e) => document.createElement(e);
+		// table structure
+		const row = ce('tr');
+		row.setAttribute('id', 'wewwa_' + prop);
+		// Text
+		const column1 = ce('td');
+		column1.classList.add('left');
+		// Input
+		const column2 = ce('td');
+		column2.classList.add('right');
+		// optional NumericUpDown Column
+		let column3 = null;
+		// div or label text element
+		let txt = null;
+		// main input element
+		let inpt = null;
+
+		// Process actual prop type
+		switch (itm.type) {
+		// only text across 3 columns
+		case 'text':
+			txt = ce('div');
+			txt.innerHTML = itm.realText ? itm.realText : itm.text;
+			column1.setAttribute('colspan', 3);
+			break;
+
+			// combo select-box across 2 columns
+		case 'combo':
+			inpt = ce('select');
+			// set options
+			for (const o of itm.options) {
+				const opt = ce('option');
+				opt.setAttribute('value', o.value);
+				opt.innerText = o.realLabel ? o.realLabel : o.label;
+				if (itm.value == o.value) opt.setAttribute('selected', true);
+				inpt.appendChild(opt);
+			}
+			break;
+
+			// system color picker across 2 columns
+		case 'color':
+			inpt = ce('input');
+			inpt.setAttribute('type', 'color');
+			break;
+
+			// Checkbox across 2 columns
+		case 'bool':
+			inpt = ce('input');
+			inpt.setAttribute('type', 'checkbox');
+			inpt.setAttribute('readonly', true);
+			break;
+
+			// Slider input across 1 column; + 1 column Up/Down
+		case 'slider':
+			const canEdit = itm.editable;
+			// create numeric-up-down
+			const sliderVal = ce(canEdit ? 'input' : 'output');
+			sliderVal.name = 'wewwa_out_' + prop;
+			sliderVal.setAttribute('id', sliderVal.name);
+			sliderVal.setAttribute('type', 'number');
+			sliderVal.style.width = '75%';
+			if (canEdit) {
+				sliderVal.setAttribute('value', itm.value);
+				sliderVal.addEventListener('change', function(e) {
+					// eslint-disable-next-line no-invalid-this
+					self.setProperty(prop, this);
+				});
+			} else {
+				sliderVal.innerHTML = itm.value;
+			}
+			// create td3
+			column3 = ce('td');
+			column3.append(sliderVal);
+			// create actual slider & values
+			inpt = ce('input');
+			inpt.setAttribute('type', 'range');
+			inpt.max = itm.max;
+			inpt.min = itm.min;
+			inpt.step = 0.1;
+			break;
+
+			// Text input across 2 columns
+		case 'textinput':
+			inpt = ce('input');
+			inpt.setAttribute('type', 'text');
+			break;
+
+			// File input across 2 columns
+		case 'file':
+			inpt = ce('input');
+			inpt.setAttribute('type', 'file');
+			break;
+
+		default:
+			Smallog.Error('unkown setting type: ' + itm.type, LogHead);
+			break;
+		}
+
+		const eid = 'wewwa_prop_' + prop;
+
+		// make input label if not text
+		if (!txt) {
+			txt = ce('label');
+			txt.setAttribute('for', eid);
+			txt.innerHTML = itm.realText ? itm.realText : itm.text;
+		}
+		column1.append(txt);
+
+		// listen for changes if input type (no text)
+		if (inpt) {
+			inpt.style.width = '100%';
+			inpt.setAttribute('id', eid);
+			inpt.addEventListener('change', function(e) {
+				// eslint-disable-next-line no-invalid-this
+				self.setProperty(prop, this);
+			});
+			column2.prepend(inpt);
+		}
+
+		// append td3 or stretch td2?
+		row.append(column1, column2);
+		if (column3) row.append(column3);
+		else column2.setAttribute('colspan', 2);
+
+		return row;
+	}
+
+
+	// -------------------------------------
+	//  Settings Helper
+	// -------------------------------------
+
+	// eslint-disable-next-line valid-jsdoc
+	/**
+	 * Callback for UI-Settings changes
+	 * Will apply them to the storage and running wallaper.
+	 */
+	public setProperty(prop, elm) {
+		// get the type and apply the value
+		const props = this.project.general.properties;
+
+		// check for legit setting...
+		if (!props[prop]) {
+			Smallog.Error('SetProperty name not found: ' + prop, LogHead);
+			return;
+		}
+
+		// enabled delayed call of settings update
+		const applyCall = (val) => {
+			// save the updated value to storage
+			props[prop].value = val;
+			// update
+			this.evaluateSettings();
+			const obj = {};
+			obj[prop] = props[prop];
+			this.applyProp(obj);
+		};
+
+		// process value based on DOM element type
+		switch (props[prop].type) {
+		case 'bool':
+			applyCall(elm.checked == true);
+			break;
+		case 'color':
+			applyCall(this.hexToRgb(elm.value));
+			break;
+		case 'file':
+			this.loadXHRSaveLocal(elm.value, (res) => applyCall(res));
+			break;
+		case 'slider':
+			if (elm.name.includes('_out_')) {
+				const inpt: any = document.querySelector('#wewwa_' + prop);
+				if (inpt) inpt.value = elm.value;
+				else Smallog.Error('Slider not found: ' + prop, LogHead);
+			} else {
+				const slide: any = document.querySelector('#wewwa_out_' + prop);
+				if (slide) slide.value = elm.value;
+				else Smallog.Error('Numericupdown not found: ' + prop, LogHead);
+			}
+		case 'combo':
+		case 'textinput':
+			applyCall(elm.value);
+			break;
+		}
+	}
+
+	// eslint-disable-next-line valid-jsdoc
+	/**
+	 * will load the given file and return it as dataURL.
+	 * this way we can easily store whole files in the configuration & localStorage.
+	 * its not safe that this works with something else than image files.
+	 */
+	private loadXHRSaveLocal(url, resCall) {
+		myFetch(url, 'blob').then((resp) => {
+			// Read out file contents as a Data URL
+			const fReader = new FileReader();
+			// onload needed since Google Chrome doesn't support addEventListener for FileReader
+			fReader.onload = (evt) => resCall(evt.target.result);
+			// Load blob as Data URL
+			fReader.readAsDataURL(resp);
+		});
+	}
+
+	/**
+	 * Show or hide menu items based on eval condition
+	 */
+	public evaluateSettings() {
+		const wewwaProps = this.project.general.properties;
+		localStorage.setItem('wewwaLastProps', JSON.stringify(wewwaProps));
+		for (const p in wewwaProps) {
+			if (!p) continue;
+			const prop = wewwaProps[p];
+
+			// some ev(a|i)l magic
+			let visible = true;
+			if (prop.condition != null) {
+				// copy our condition string to modify
+				let cprop = String(prop.condition).split(' ').join('');
+				// remove whitespaces and split to partials by logic operators
+				const partials = cprop.split(/&&|\|\|/);
+				// loop all partial values of the check
+				for (const part of partials) {
+					let prefix = 'wewwaProps.';
+					const onlyVal = part.match(/[!a-zA-Z0-9_\.]*/)[0];
+					if (!onlyVal.startsWith(prefix) && !onlyVal.startsWith('!' + prefix)) {
+						// fix for inverted values
+						let replW = onlyVal;
+						if (replW.startsWith('!')) {
+							replW = replW.substr(1);
+							prefix = '!' + prefix;
+						}
+						// Smallog.Debug("replace: " + onlyVal + " >> " + prefix + replW);
+						cprop = cprop.replace(onlyVal, prefix + replW);
+					}
+				}
+				try {
+					visible = eval(cprop) == true;
+				} catch (e) {
+					Smallog.Error('Error: (' + cprop + ') for: ' + p + ' => ' + e, LogHead);
+				}
+			}
+
+			// get input dom element
+			const htElm = document.getElementById('wewwa_' + p);
+			if (!htElm || htElm.childNodes.length < 2) continue;
+
+			if (visible) htElm.classList.remove('hide');
+			else htElm.classList.add('hide');
+
+			// set its value
+			const elm: any = htElm.childNodes[1].childNodes[0];
+			switch (prop.type) {
+			case 'color':
+				elm.value = this.rgbToHex(prop.value);
+				break;
+			case 'bool':
+				elm.checked = prop.value == true;
+				break;
+			case 'slider':
+			case 'combo':
+			case 'textinput':
+				elm.value = prop.value;
+				break;
+			}
+		}
+	}
+
+
+	// -------------------------------------
+	//  Wallpaper Interface
+	// -------------------------------------
+
+	// eslint-disable-next-line valid-jsdoc
+	/**
+	 * Send one or more properties to the Wallpaper
+	 */
+	public applyProp(prop) {
+		const wpl = window['wallpaperPropertyListener'];
+		if (wpl && wpl.applyUserProperties) {
+			wpl.applyUserProperties(prop);
+		}
+	}
+
+	// eslint-disable-next-line valid-jsdoc
+	/**
+	 * Send paused-status to the Wallpaper
+	 */
+	public setPaused(val: boolean) {
+		const wpl = window['wallpaperPropertyListener'];
+		if (this.isPaused == val) return;
+		if (val && !this.pauseOnUnfocus) return;
+		if (wpl && wpl.setPaused) {
+			wpl.setPaused(val);
+			this.isPaused = val;
+		}
+	}
+
+
+	// -------------------------------------
+	//  UI Color Input conversion
+	// -------------------------------------
+
+	// eslint-disable-next-line require-jsdoc
+	private rgbToHex(rgb) {
+		// eslint-disable-next-line require-jsdoc
+		function cth(c) {
+			const h = Math.floor(c * 255).toString(16);
+			return h.length == 1 ? '0' + h : h;
+		}
+		const spl = rgb.split(' ');
+		return '#' + cth(spl[0]) + cth(spl[1]) + cth(spl[2]);
+	}
+
+	// eslint-disable-next-line require-jsdoc
+	private hexToRgb(hex) {
+		const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+		return result ? [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255].join(' ') : null;
+	}
+
+	// -------------------------------------
+	//  AUDIO PROCESSING
+	// -------------------------------------
+
+	/**
+	 * Request microphone from browser
+	 */
+	private requestMicrophone() {
+		navigator.mediaDevices.getUserMedia({
+			audio: true,
+		}).then((stream) => {
+			this.stopAudioInterval();
+			// hack for firefox to keep stream running
+			window['persistAudioStream'] = stream;
+			this.ctx = new (window.AudioContext || window['webkitAudioContext'])();
+			this.source = this.ctx.createMediaStreamSource(stream);
+			this.analyser = this.ctx.createAnalyser();
+			this.analyser.smoothingTimeConstant = 0.35;
+			this.analyser.fftSize = 256;
+
+			this.source.connect(this.analyser);
+			this.startAudioInterval();
+		}).catch((err) => {
+			Smallog.Error(err, LogHead);
+			if (location.protocol != 'https:') {
+				const r = confirm('Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS for this feature to work! Press \'ok\'/\'yes\' to get redirected to HTTPS and try again.');
+				if (r) window.location.href = window.location.href.replace('http', 'https');
+			}
+		});
+	}
+
+	// eslint-disable-next-line valid-jsdoc
+	/**
+	 * html5 audio analyser gives us mono data from 0(bass) to 128(treble)
+	 * however, wallpaper engine expects stereo data in following format:
+	 * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble)
+	 * so we do some array transformation... and divide by 255 (8bit-uint becomes float)
+	 */
+	private convertAudio(data) {
+		const stereo = [];
+		let sIdx = 0;
+		for (let i = 0; i < 64; i++) {
+			stereo[i] = data[sIdx++] / 255;
+			stereo[127 - i] = data[sIdx++] / 255;
+		}
+		return stereo;
+	}
+
+	// eslint-disable-next-line valid-jsdoc
+	/**
+	 * Start the audio processing & analyzer
+	 */
+	private initiateAudio(data) {
+		// clear up
+		this.stopAudioInterval();
+		// create player
+		this.audio = document.createElement('audio');
+		this.audio.src = data.name ? URL.createObjectURL(data) : data;
+		this.audio.autoplay = true;
+		this.audio.setAttribute('controls', 'true');
+		this.audio.play();
+
+		// insert before marker
+		const markr = document.getElementById('audioMarker');
+		markr.parentElement.insertBefore(this.audio, markr);
+
+		this.ctx = new (window.AudioContext || window['webkitAudioContext'])();
+		this.source = this.ctx.createMediaElementSource(this.audio);
+		this.analyser = this.ctx.createAnalyser();
+		this.analyser.smoothingTimeConstant = 0.35;
+		this.analyser.fftSize = 256;
+
+		this.source.connect(this.ctx.destination);
+		this.source.connect(this.analyser);
+		this.startAudioInterval();
+	}
+
+	/**
+	 * Start the processing loop
+	 */
+	private startAudioInterval() {
+		const data = new Uint8Array(128);
+		// 33ms ~~ 30fps
+		this.audioInterval = window.setInterval(() => {
+			if (this.audioCallback == null) {
+				this.stopAudioInterval();
+				Smallog.Error('no AudioCallback!', LogHead);
+				return;
+			}
+			this.analyser.getByteFrequencyData(data);
+			const stereo = this.convertAudio(data);
+			this.audioCallback(stereo);
+		}, 33);
+		// tell Wallpaper we are sending audio
+		this.applyProp({audioprocessing: {value: true}});
+	}
+
+	/**
+	 * Stop the processing loop
+	 */
+	public stopAudioInterval() {
+		window['persistAudioStream'] = null;
+		document.getElementById('wewwaAudioInput').setAttribute('value', '');
+		if (this.audio) {
+			this.audio.remove();
+		}
+		if (this.audioInterval) {
+			clearInterval(this.audioInterval);
+			this.audioInterval = null;
+		}
+	}
+}
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/WEWWA.html b/docs/WEWWA.html new file mode 100644 index 0000000..0e07852 --- /dev/null +++ b/docs/WEWWA.html @@ -0,0 +1,2891 @@ + + + + + + + + WEWWA + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

WEWWA

+
+ + + + + +
+ +
+ +

WEWWA(finished)

+ +
WEWWA
Wallpaper Engine Web Wallpaper Adapter
This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine Web-Wallpaper project - so you can test, run & configure it from a normal web browser.
REQUIREMENTS:
- HTML5 Browser
- the "project.json" needs to be in the root folder like "index.html"
- this file needs to be included/built in your "index.html"

FEATURES:
- automatically detecting if the web wallpaper is opened by wallpaper engine or browser
- if opened by wallpaper engine, nothing will happen
- if opened by a browser:
- use a ServiceWorker to make page always available offline
- automatically load the "project.json"
- parse the settings, languages & conditions
- add respective html elements for each setting type & condition
- put these elements into an option menu which can be hidden
- check localStorage for already saved/customized values
- apply all settings once
- react to changes made in the ui and update them in the wallpaper
- save changes made in the ui to localStorage
- Annoying Cookie Popup (Thanks DSGVO)
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new WEWWA(finished) + + +

+ + + + +
+ Check if we are running in Web-Mode if yes => iniitialize, else => do nothing +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
finished + + +function + + + + Callback
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
To Do:
+
+
    +
  • - inject "audio processing" setting lighthouse: - image explicit width/height - cf longer cache policy (2d?) - +
+
+ + + +

+ View Source + + WEWA.ts, line 72 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + +

Classes

+ +
+
WEWWA
+
+
+ + + + + + + + + + + +
+

Methods

+
+ +
+ + + +

+ # + + + + addMenu(lang) + + +

+ + + + +
+ HTML Creation +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
lang + + +string + + + + WE language
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 290 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + addMenuAudio() + + +

+ + + + +
+ Add Audio Menu +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 536 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + addMenuFooter(ce, menu) + + +

+ + + + +
+ Adds the Footer Link to the Menu +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ce + + +function + + + + create element
menu + + +Element + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 443 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + addMenuHeader() + + +

+ + + + +
+ Add preview Image, Title and Link +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 613 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + addMenuIcon(ce, menu) + + +

+ + + + +
+ Adds the Menu Icon +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ce + + +function + + + + CreateElement
menu + + +Element + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 321 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + addMenuSettings(ce, proj, self, lang, props, menu) + + +

+ + + + +
+ Adds the actual Wallpaper Props as HTML +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ce + + +function + + + + Create Element wrapper
proj + + +Object + + + + project
self + + +object + + + + this
lang + + +string + + + +
props + + +object + + + +
menu + + +Element + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 353 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + addStyle() + + +

+ + + + +
+ CSS Insertion +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 161 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + applyProp() + + +

+ + + + +
+ Send one or more properties to the Wallpaper +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 897 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + convertAudio() + + +

+ + + + +
+ html5 audio analyser gives us mono data from 0(bass) to 128(treble) however, wallpaper engine expects stereo data in following format: 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) so we do some array transformation... and divide by 255 (8bit-uint becomes float) +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 972 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + createItem() + + +

+ + + + +
+ Create an HTML Menu Item from project json property +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 631 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + evaluateSettings() + + +

+ + + + +
+ Show or hide menu items based on eval condition +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 829 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + init() + + +

+ + + + +
+ Initialize the Web Adapter +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 128 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + initiateAudio() + + +

+ + + + +
+ Start the audio processing & analyzer +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 985 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + loadStorage() + + +

+ + + + +
+ Load last settings from localStorage +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 145 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + loadXHRSaveLocal() + + +

+ + + + +
+ will load the given file and return it as dataURL. this way we can easily store whole files in the configuration & localStorage. its not safe that this works with something else than image files. +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 816 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + makeMenuLocalization() + + +

+ + + + +
+ Add Language Menu +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 477 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + mergeLocals(local) + + +

+ + + + +
+ Add missing default localization strings +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
local + + +Object + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 424 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + requestMicrophone() + + +

+ + + + +
+ Request microphone from browser +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 942 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + setPaused() + + +

+ + + + +
+ Send paused-status to the Wallpaper +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 907 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + setProperty() + + +

+ + + + +
+ Callback for UI-Settings changes Will apply them to the storage and running wallaper. +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 760 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + startAudioInterval() + + +

+ + + + +
+ Start the processing loop +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 1009 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + stopAudioInterval() + + +

+ + + + +
+ Stop the processing loop +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WEWA.ts, line 1028 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/WarnHelper.html b/docs/WarnHelper.html new file mode 100644 index 0000000..a98c3bf --- /dev/null +++ b/docs/WarnHelper.html @@ -0,0 +1,926 @@ + + + + + + + + WarnHelper + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

WarnHelper

+
+ + + + + +
+ +
+ +

WarnHelper()

+ +
Seizure warning display
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new WarnHelper() + + +

+ + + + +
+ Create and prepare once document ready +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WarnHelper.ts, line 36 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + +

Classes

+ +
+
WarnHelper
+
+
+ + + + + + + + + + + +
+

Methods

+
+ +
+ + + +

+ # + + + + hide() → {Promise} + + +

+ + + + +
+ Hide warning +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WarnHelper.ts, line 107 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
hidden
+ + +
+ + +Promise + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + injectCSS() + + +

+ + + + +
+ Make custom style +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WarnHelper.ts, line 51 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + injectHTML() + + +

+ + + + +
+ Make custom html +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WarnHelper.ts, line 71 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + setText() + + +

+ + + + +
+ Set html text value +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WarnHelper.ts, line 79 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +

+ # + + + + show() → {Promise} + + +

+ + + + +
+ Show the warning +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WarnHelper.ts, line 86 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
hidden again
+ + +
+ + +Promise + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + updateSettings() → {Promise} + + +

+ + + + +
+ Settings have been changed +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + WarnHelper.ts, line 123 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
finished
+ + +
+ + +Promise + + +
+ +
+ + +
+
+ + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/WarnHelper.ts.html b/docs/WarnHelper.ts.html new file mode 100644 index 0000000..2c188c9 --- /dev/null +++ b/docs/WarnHelper.ts.html @@ -0,0 +1,261 @@ + + + + + + + + + + WarnHelper.ts + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

WarnHelper.ts

+
+ + + + + +
+
+
/**
+* @author hexxone / https://hexx.one
+*
+* @license
+* Copyright (c) 2021 hexxone All rights reserved.
+* Licensed under the GNU GENERAL PUBLIC LICENSE.
+* See LICENSE file in the project root for full license information.
+*
+* @description
+* Displays a seizure warning image centered on html for a given Time.
+*
+* @todo
+* - add trigger warn languages to project json
+* - add trigger warn as html
+*/
+
+import {CComponent} from './CComponent';
+import {CSettings} from './CSettings';
+import {waitReady} from './Util';
+
+const ELM_ID = 'triggerwarn';
+
+/**
+* @TODO test getting text
+*/
+class WarnSettings extends CSettings {
+	seizure_warning: boolean = true;
+	seizure_text: string = '/* <!-- ## ERROR ## --> */';
+	animate_seconds: number = 1;
+	wait_seconds: number = 10;
+}
+
+/**
+* Seizure warning display
+*/
+export class WarnHelper extends CComponent {
+	public settings: WarnSettings = new WarnSettings();
+
+	private element: HTMLDivElement;
+
+	// promise behind showing the warning
+	private showResolve: any;
+
+	/**
+	* Create and prepare once document ready
+	*/
+	constructor() {
+		super();
+
+		waitReady().then(() => {
+			this.injectCSS();
+			this.injectHTML();
+		});
+	}
+
+	/**
+	* Make custom style
+	*/
+	private injectCSS() {
+		const st = document.createElement('style');
+		st.innerHTML = `
+		#${ELM_ID} {
+			object-fit: contain;
+			text-align: center;
+			max-height: 30vmax;
+			top: 25vmin;
+			opacity: 0;
+			transition: opacity ${this.settings.animate_seconds}s ease;
+		}
+		#${ELM_ID}.show {
+			opacity: 1;
+		}
+		`;
+		document.head.append(st);
+	}
+
+	/**
+	* Make custom html
+	*/
+	private injectHTML() {
+		this.element = document.createElement('div');
+		this.element.id = ELM_ID;
+		document.body.append(this.element);
+	}
+
+	/**
+	* Set html text value
+	*/
+	private setText() {
+		this.element.innerHTML = this.settings.seizure_text.replace('\r\n', '<br />');
+	}
+
+	/**
+	* Show the warning
+	* @return {Promise} hidden again
+	*/
+	public show(): Promise<void> {
+		return new Promise((resolve) => {
+			// dont show
+			if (!this.settings.seizure_warning) {
+				resolve();
+				return;
+			}
+			// wait for resolve by "Hide()"
+			this.showResolve = resolve;
+			// make text
+			this.setText();
+			// show it
+			this.element.classList.add('show');
+			// wait some time
+			setTimeout(this.hide, this.settings.wait_seconds * 1000);
+		});
+	}
+
+	/**
+	* Hide warning
+	* @return {Promise} hidden
+	*/
+	public hide(): Promise<void> {
+		return new Promise((resolve) => {
+			// hide it & wait
+			this.element.classList.remove('show');
+			setTimeout(() => {
+				if (this.showResolve) this.showResolve();
+				this.showResolve = null;
+				resolve();
+			}, this.settings.animate_seconds * 1000);
+		});
+	}
+
+	/**
+	* Settings have been changed
+	* @return {Promise} finished
+	*/
+	public updateSettings(): Promise<void> {
+		// update text
+		this.setText();
+		// fix for instantly removing the warning while it shows
+		if (!this.settings.seizure_warning && this.element.classList.contains('show')) {
+			return this.hide();
+		}
+		// whatever
+		return;
+	}
+};
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/WarnSettings.html b/docs/WarnSettings.html new file mode 100644 index 0000000..5f9905e --- /dev/null +++ b/docs/WarnSettings.html @@ -0,0 +1,256 @@ + + + + + + + + WarnSettings + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

WarnSettings

+
+ + + + + +
+ +
+ +

WarnSettings()

+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new WarnSettings() + + +

+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
To Do:
+
+
    +
  • test getting text
  • +
+
+ + + +

+ View Source + + WarnHelper.ts, line 24 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/WascBuilderPlugin.html b/docs/WascBuilderPlugin.html new file mode 100644 index 0000000..92a6273 --- /dev/null +++ b/docs/WascBuilderPlugin.html @@ -0,0 +1,435 @@ + + + + + + + + WascBuilderPlugin + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

WascBuilderPlugin

+
+ + + + + +
+ +
+ +

WascBuilderPlugin(options)

+ +
This is a webpack plugin
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new WascBuilderPlugin(options) + + +

+ + + + +
+ Intializes the plugin in the webpack build process +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
options + + +wascSchema + + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + wasc-worker/WascBuilderPlugin.js, line 48 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + + + + + + + + + + + +
+

Methods

+
+ +
+ + + +

+ # + + + + cleanUp() → {Promise} + + +

+ + + + +
+ delete all files in the output dir +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + wasc-worker/WascBuilderPlugin.js, line 187 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
async finished event
+ + +
+ + +Promise + + +
+ +
+ + +
+
+ + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/WascInterface.html b/docs/WascInterface.html new file mode 100644 index 0000000..cc5bd24 --- /dev/null +++ b/docs/WascInterface.html @@ -0,0 +1,251 @@ + + + + + + + + WascInterface + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Class

+

WascInterface

+
+ + + + + +
+ +
+ +

WascInterface()

+ +
The shared interface for loading a module
+ + +
+ +
+
+ + +
+
+
+
+ Constructor +
+ + + + +

+ # + + + + new WascInterface() + + +

+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + wasc-worker/WascInterface.ts, line 5 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Bold-webfont.eot b/docs/fonts/OpenSans-Bold-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..5d20d916338a5890a033952e2e07ba7380f5a7d3 GIT binary patch literal 19544 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Bold-webfont.svg b/docs/fonts/OpenSans-Bold-webfont.svg new file mode 100644 index 0000000..3ed7be4 --- /dev/null +++ b/docs/fonts/OpenSans-Bold-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Bold-webfont.woff b/docs/fonts/OpenSans-Bold-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..1205787b0ed50db71ebd4f8a7f85d106721ff258 GIT binary patch literal 22432 zcmZsB1B@t5ubU^O|H%}V|IzIVNI zUovCM*w)bDm$Uix&jbJf0&20h={9zAA^05!;@9Ta9)O418En_g!QA$j%|T zg7y+LH+25>h2!|O`Oo%0Aeh^Dn*DMD0007R000ge0Uny~7N&+K0045Wzx^z~U;{Kx zUbpxqf4R$F{l9sTz@vgjSlGIF007AU#s~B}CU7TXuFRs1z45P|qR4N2OTXCll}{hH zHT3wsuJV8Pgy25_69Vzr8QPlua=-Bb&i}^9U_Kjd;b8CV0sx?j@XNjYjt5W_dcEY} zWcur?{$H$r|HFd_(WSeo(QnM^|9*9_|6rl7So13Ze*rMbn?LiP91}v%{ZCFUVQhP> z8ylDy80-QYL4qL|7#V={y9-PL9W(yUI~b4<0Kj9tDn(W%NgQM3r-SAi%{IQ-av{#b zm?Dp*nUWE(`7{EcC}s)ta^1+9Uj`lvS<-m^uZMv8f-v%ehSe}U)}pB5vjGC6Uy~pm zo)<1qh;kgVTrs$D``1)&z8ke|;_(>$1Je!j%!vOnt{S4G>G`aABr9vrN*+4@PrG+q zdH3aZlXjCg-utrN?)PA6A(Aic*r{P)fItNfh`QJTc? z3wgp|$4hT`N(iVlzs(@58kfEk!62o^Q$flqq@=t{xl6XxO=$TCkbN0bkG!jwEbQN4 zG2V(|AGxWwXsuk-^?T%XAZ@~-ovUcv=&a}s0@$uWPKYo9;IKW2M`U||9p*tE=o13y zAO}3UTRRB4eo~B3#8#jJ2h?E$oa*=!uFZf9hm1DKeep&;V=p~b&jPH{5LgBA@Apns zU_VKVVEcdkU^~M2p8z9$y^ucg{gfQAU$62E{9_n|TCq4qgET=@+bg~A5}0o^Z#JVV z0qRI-PMZJEiE6Zg;GOQ;a2q|YsR@`&xDGOhGncu2d?Pj-GduAh$N_@M0V6IXBF<8R zxjfTXUW5hxM5`WGGjy>!(C%ba9^je@u0M9bG`-6VPM;@*UhaZwS{dYJWn~}}ibs}G zwGYxwzK4<->i3DRk}gn0r*b}@NcD5zt|~z4eUPlFFr-kBCng*diUrGxHMPqQK9yIo zB)B7F{t676O}rd4M%_4i?(Wg!N5}Pcv!4?>x{ffiV@XWmaoy{%8Wm5Ska0TN1*tUF4 zR};ELu9o%iR=|sY^G~PFaL86`dKghU?-lE#d&z}pZ+O3EY*1UyOcxQKcc*>kZrR#Zgl0UbrqyO(KU-@)HSW=yLIKuRVv{d z)L3=2Hasz^73ld^tUTeWl^AnXdtrW!p5f0DAcnD2vgr=9S&I~S<@~f7FLK8=U8MLO zub`KNmnLdxsr4ZF!hIad$A;=O|K_Ow$zev}MxzD>j*btIhJU51X~qo|BvFieSwmA2T)~V@&E$JN5n$?FPQ>^cms6; zfC7Mkrh_v7CS3ggk-&2RW`Lg%KtRwCV8EatKtLe706;ea00i21Z!|FQ0gaGB zKz~VrOzxN#89&WgOkm6^4Y-C~qRwK0QUk*SlL9jX69Ur%y91L0ql7wzBKomJi@;%e zG{1kqGe)2ndjLwQA*!PU1qB3!1i{KDkVMgm70?fUYJTv4_#gfEfBJvAe=xqgzdnxp z#=yn#aC{tg`?kS5@NB$l@B0G5ZQ&#FG#fHg>&5qGh z)Rx(r-JaoM<)-PX?XK~%^|txC{k{SJ2=)=?8SWv*E6y?2Io?4=z}Q}8Z6%sdYIjZ!tQ;*e zRIV=l%LF$%S>}_lvdZ#%9eu)fzuxX_O5EF>BcH+N^?ORsyMN{lP02pquKtEZ{wS6+ z{>Nl~eJMO5hr+~wQv+lL0&obKy!YR;5de)ohS3-N=ZXysoB<(?13bWw7`xpATWS8& zW0+`8`TYadZ|-1-3If172LD?bc&ulsTDmWYp(J;b#3s&?LW8Z=#HgW{LQb+<(Vuo-en}s5k&k>}Q!XMicO zVLg=&(uGl9(Oo$-PVIkRw7^8@GMS=KQ@O$qUR{@LG>4z%E!?>(RP5ICNkw(ERwIDN#rrPuiBq|9tPRn(cB5|zN0 z+L9lPC|rbz!sI*m2=9PF9G?=@X;lErA)3sio}aE{WzoYnwr`zLmy*4ZoE5_#dQm=g zC(_*GfX1p4-?zc*sJ1@h3(_jz>ROHG#4Sg0^v}t0&(b7^d1(As^L{`1LYMo-F2HjD zeqT(fv)&@3nD4uRV!95htYU$lM|G7zS!|Ii%P8x;jKaF^F2gA7JuNZyliD^z{KDCJ zK*)a8F)I6k=d{orx7mnKz+NR}w+`mCpeJCb6|>n$E#`U&!2&x!T|yO@YiaT{&{|c= z3Z%(8|5y|;))7v4QGtx>y1Y!~kMgq=L60+96p?*hucL$PZn@QbyLaZMzoo@|9$Gcb z9-9<)$1r~|8$5k)5BJl|?%JW@oT`v42w!TT1OP^14UY70c}YUOf&0zbeJbDwiU zc1g)Mn~}wre&(Y+E)n_0n`et-f_6n$OC-fLX!9TMr*@=_>sLW%QS$j=xa*OLc2g*0 zVSiNq1+}DSY_r<|I;pDKcGSGpn-9{x$%=!p#l$i%j9W0JtY>)GiVCF^d{a`vB|=yW ziYcDMco4K!=wK_HE4-EU;8~s*1~xQdXkKF%LahX)F6vI>xcePmh4uQW$A09k3o&Oz zxV&TX7llW8MS-6SxUF7;U74X&^7$Fxf%4@=v#*L8R@uSj5baVQ>r}g#+|VQPTe`*; zHk{Ur06Z$b?5u?96k|K%I7W=A>{~_v-SD_QMwOOLPuNFUVq>JLJ7S`*^FCgtTZ_JF zPm1%zX#3B4ZcB{LoioXCi|8N!6M@T=%0Mr3CIn+ZPH3!w)&4`c0aqCMi(7vgxt|_b z=%_=@D~rr2W&G;+XsWh}lo4IK`iW4yCeCuV`BiZX8%qzPSX{i=kQ5A@zg7OX{?XpO zx;lRWI9Qx8$@1BBOG~_3+efTyu&0wn0(6}(IdB8;0;FfzN2;HEfDCwFM%$nra&Q81 zognx~!*-dS>;Qe_;QG)H5nx6MS4mIcdV!rF@DhY;#o_vho!9`oNy2uiogj>yAdsBw zfO*Kmb|E=I^b>_|W8y22(|V4C*aEs6PRSIkO2DGn(9+_qk)Qd{Q+y2&*TT@^y-W_@ zgWr>&rN6d`l>BSM7x7~@|0($I_bd4~hcD{W5Iv>c6}gcdCHFaR&-LY88&+BTzRv&w z0Dpb};62u-e603-?>W9ym$SMD!*6Uxk4IhITVfXue^lrzwEI6A4uh1-DI^VaSIDCN!Bx#_}2`m_w3&xgi4^FsaE+qj- zQ4%UsktG=;O@8Za=2(jd)*A!vf(m-OqboU|8Vznb31Ud8!sc#oZ?3j7!OcvF)%kQd zJY`fJu(sy79GVv^6X{(JXHSy*1FTM>DfC(>lL8sfs;P{ML$J2kit`r%xO+G4@@wsp z^;3Fn?HxAefF6z>9p7LaE z{j~1BVfTCvDBEx(47Zd+?M~MEJcD;TDb(+d&pJ@`^XVI1d{>e!ttZy!4)k7$$e4~k zc|wI-l02;t`wad33Pf}K?EIyun1pl~Lso_DR#Tc(B&C#OL97rNB1G%kh4g+$YTPD5 zE<@SzI6!$xXFG5*pbEOx_RqD#Y(;G;!D*zs^(S-r<2Xz!R3GLIox)N53>-ag&qeXg za5CQN?HRYUe3#PCf&9yLLyN;jb>aGPpmxYxMRCms+UP#0cm{uRPFFnsNjEF>%zc4z9w!+P%u^7nX z{c$W-i|4HxWx>n&D3VKLAyNqqNu}jFwg8&3@e>JQHqw1}TU>GMfAVuz?@C5dXM(-H z4;^qua~M^SgZfM)zl6P<4nV2RsWA6Gs1NF9HR1uwY5KhM8 zUV_kZ)IWgU50B%pQ*)sGH@i&-;7UFBNZYH9g6s=3hqCxn#{!R2q8>8%KRz$ycV}1p zyELjVZSvmDOZa}?jX$Fy(n{NX#7IX6RFWci=24s;85AY&Je9ZZprinEDUwcQo)ARy zmReEc`6P*!0<tE_`L^9G#rd~^DcPNZe)+yc zTf8mwN4&_GaC@cpR|Q2$hkY5jY)ua3bk@1djL!A6dp=e4XfvAo!*cU_uOPX3_UF$f zz6*M`I6nRf^vmNjPWRfL^aRuq?`0MeCkfUO`cObP7j%%Smu%NUpb}gGdv{i~Vb6-1 z8A9-;K!Zee(axpW7PRGzI``f)MG)2ZdnK|!SAR&j1W)NJ?veLt9&WebvXTa zxc$!FY2XQF4Tw!qRwb`X$W%~^9+D9hG$17_07T7_0(0<+CDDplB9wUSKn*hs z4H(c5wzAP?n|!XN#rJ=ooM$FqT?UYuP|LcU8%_anv!O$25OyZuJ~JYoMCim2=1Yz` z`Wlq^%!66Pg~AP`QUl8eC=={cpo$Pmz6cpVFapR1ii52RoG^aqcU*>viX9+Y_Q_oh3X z*uG)GfQ#7RF-X>hMK{cP%tOWW@)nn%ME z{;oZQH;LrW+SnCg*>IR{;pEAKse?C$I4|ZPn)%Bia`-@(vPIMZwm6Rsa#y!;}VlCCIS}Xz=8T%q? z3yW-Q9#XDdJPBNVLqCCOM4IO2sJSrUV+p7bu*IKmmVY~-I&##5ffK}W7I_R`ZJ~B8 zDzRGL3&mw|HdZ?CsoZuNZQks*d|(aP`X1Ujj0MzS_?6h{TeSzV5%k^dN1_$~pzj+& zP7)-+g5S*oDhYN>Ra{ge`_eQN5R#B|P@s^sU^Ugs6$?1qtn7_jR}LOboyU&Q{>n={ zn>bL1^Nf@o3;gjQF4j36OErBNR;9l-xoPmv++sc73N69gXtaKxoa%Xh*iCMl*a2E8 z$sJor{T?eB{&5?cTNn_WptQ+!y*RD0F1EW|I|&kZchnz<`plqQ?iYj-dZVH;)q%e5 zq;M)IR>IVTWU`}|L{g&w8=o|57`Sv;yKJ3+;ZUc4*Ubj%tvcSrT8WBO%WjMLDtc0E zM^I|1gGn^GeK9)81Lp?fjg{QcBGW(hA68WDD?Vk~4Dg}uO z0?kB>r--+T*K{JSmu!hh<!R6BTSVNYfECYc{7hM+!$yzZQmgC6~uW zZnb|Cc!)OUTkUIwBgCsN8{e@yl@NlT!0SPkIQ&!=sfdUBDJ*9u7ZUA9xT|eA-EW~+ z#yJO{!@XROpy7Drp-u|pf`cNhxTIXs;I7FONh62E8j7XCz^?Z*c|o4xb!t zMtJ4H4-Ob_A_g#9^IQr105w8Hj~}5!wB|<~@K5)YmbB+Sbkak4{TPRdpyWc1(hAiV zivRkdi7ORE@DcVWP7?y$KNz=G>=KU^=@ec_O&p(L2pn z4GHD$C3yl|LlL-Phh|Zw+e^n|cOa_VZIKed*`65LOG66lZXG zjaF}J(?v;!VdWR@_i)+Ai!^wgU6k;l*XmVtl0F$&i`GF=PrefV95h8Gfw zzk8?5y$aX-b{cp@J~>06@6p?$u@;knBJ36FG?nSq$W6iViWOCFLU}~U-r@@eOc;tG z3=_LFJF$4li3fAUyUPe9xll}Ox;1BGUs@^x7F>P z78>|xSe-A9jUJ6wifg3^EQTr^O%;KHN!3aeXVCYn83TNdoQ$lPyx8=Whw}^z3sJsZ zp}4(d_o=ZBGUAV5^e>11yzs-?2)dTMz+SAk*|h%W=ElpkG41#?`U}mv33HLH z-t#i~d}U-EvAxaK3|dT1YvN51XDM-9uFgnezryUF>m+62c!pea(qso-{0OlDx|FDV z%I1-@7z&mFeN$XFkT$~>zA zpYSh_^tQ0N6v9&$wl82iueaqC0ed1BynCs%m`|hV~9|(NI%33RI)SkS>YL3YZ755sj4KR*1X7uCzQ*QWxOudkw z4nC$X0iLo*y+|aIBf&;LbnNKSoIaE78f9`z_8;d-u`GzRuD(?y-0DGu>Ua|akSGU9 z@m5=c0~B) zk;VpQF0ST}PQDsElr@Kp{R9Yjk%1WTkQl0Z&(o4do3*%?y3|$YS|mGO&%@=W9`47h zZgqQ0gOZ{^HDz~xn$R)^JUl#aLy(VWd~31XL*BQZ77 z>QoR$% zf=;0@rnhUCS@lFpOJoAt)0WVp7&7`>8r|&!>7Gwhw8s)Ma6DT8Jqr>qis4O3ysFjg zfJp9w#{*-GQ55r3wL@Ho+}z8reIjNs0gTX$G%W{Zo}t#{Z2_g|0x#Pu+HP4?|Dg0{ zI?u+Qe8QepC|-)~1VIXn)pjF8ZOSMZR4joA#uc$JraoxMJbdEOYwhlsOOVO`h=QZ{ zx6`I-?vI-nakT0j?A9n>3XNE^NcPO~lpSu+zm>5k^og_BPVYWXOG$2jILNHw17}ST zxELO1)ips39Gp5jn5$Asx<5|gTWelD0v*BAD@J{^>U9TGRih8mH3H{ZE@9R1uY9jM zgVoj6!_}DatH~ZNn&Qa;M%i{z10DiznN?;Rw=-7%V3J?W_lw~5d_m3Xj%qH8$ycS= z;PC=1U(E^6W68Ta0Q3je@HbrIJ2g*0*r>E)y2hluKB>WAV@;v{m06=8>_y;^e1i)|*Puw%qp=B}PseK!q6F)8{W?K;CZfE}9m?!r=Q%Ei@e zLaS$w;y-db|JWMMNVXl2v&ULyZFp&{z3oMWghi$uD5j5SD#SgH#k4c@9(@HzVB8?4rie}u5<)+K#$rzQ+`;DAm7BKvs9f- zP2hVNfLQ2n`gxcQT$YTFESjtFe{EZ7xbET`6Lb~U8fnN`{?r4ySGKv{>_9zyuQ4~2 zlXU1izP*0=WUo=s^Z1wC>3~-g%u4MkG*bHM>Yif7XB*l#Xx>BkTmg(@@b#dYcH!l; zIB$(77Qe@f22*`*$X)7%$=96(OqGqdp6jHYDTc|G>Gw^4$NLU%2L^)sH({aLNDs9? zy!<&yXlydwgP!^JYFMni(XBQN6bd`wiP_wu-`ikCdN|-A9o$9q|0^6KIxk9LR%b&U z6=dYl`k>-0Ay3y-iTSLjwq?#GW6RzzbL1=^uIh1K5PTxM{$v`sk&>&;N0|u5fOg!S z6a?-s3Ks{A7{PvS@O%M$45WF5*?{kQCj9qhq|<|S@^y?#Q4_nmeliG^=!A3haoAYtydfBFgB{4)+H?Y3@?9 z8T98eK)I4VI+PCsMWq%feakD_PkP7ZD@9A&x&PLb>{(ojLQzzDDJ{{h1D12_&py+i zFuDMq;H1fI(=i62@&aRRv?jbl-ojeBDd-dP=uP@Lmkct+_;n~~C2y+^pHjA#U@;KoUP1oIX(P(p zIC(z9j-@DZdb_?8+E)jFj z0e+2f8Pmf#d{st!VAj#Eq!mUw!8E1dOsW3q2c3j$xwu0n9E;gbF^1l0@x4vX$FJ^O zFiUf3PTj?In$HllX6^D;9*mP+I8JVJA6p*CG3HSv(FwJ($Sc2p{J_FT@I|KO;4A1y z;s;?EKAr=wRX{y|Ffw^oV#bSlk#F4Qe1WG^`%VG158*qm=pAK!pm{Zzu%6WMJ)1eS zt>Drw3C7rRTkGHdNC33JS%ADUrj;u;u_19A<ZcSR~zNw^YI(s69dZI!?x? zzuJ25l}3KakVb~@Sr$hOd`eNQ3mV6*q{D?PTY_VM4(uy1NFqna=trpsiH--v3G zIDuP=(4vajEL%7h*AFGXv35vURw6E?Dq|yf87OolrKFfRJ}9h+6~^9(uO=ZMrWlKe zWid~ur5iRnK0$!03)&h~mUGjQS$x-v(KaYSqj51eSVS3{lvoDN@$qx`fl+^1E;j<^|xP`Ol3u2zY-0(J%`T0FuJfXtjod9%f^u-i^ygAtZ?~; z5H#9*B^uYq{infvq!LT%yD;%NNM#h)i)<;5%UwOr$E_?3{w>P+uX*U(#|YuZ{$K<# zXlBf^1j;7!IEP>B`Y^5gzxet;=VLU!vQ7m#im1Qk`IT^9XX#yi`DoTil=Ap9>43Qv z7p+ny>o8K2gcMlQ&>Eu{jG5EN5v<1&Kz#u%y42ZsVhJ2>mYtLEx4N$pR)(3paxuGn zx@QOSJt3MyO^rPse4-yugV8__o)2BU7?=NW6ptFy%oC}BLly*vE?|WFx~*DNij71H>7#=RaGaIuRFGojZB^hK2`W#2GKJG#yKK)98?a4Y z3wpi%S`Oh||B8XdRUVJm&LHlA_+`@aWDcjZpET+_I~!hZgZ&Jj zbNcTRrY4DI{l1K&U8G9>A0XiPJfoDm{-|SeT`8N@e2&iVQBU*}9l>~xJCwYv$cIFk zOCat}%Z2NKndzF+3XD~3nEA~V()rDiit_E%<%7gULtpT-H{E2;Bg@eW8zl)LlLk6W zH~>GV8qE2aBn!#hK%E2{zGQA+tpfhPG3{Bo*X6`uK`ORMWd^hXTCyrjs#u&uO^PT5 zo1+@UV6_tP{((BqKCp2h!e1XK=!fn%p$(I8ufAPOvZtx7Eb&AafD}}|gMa~-h*+}x zKepVUZo(!D56LdUKYLSuOTM~KisGW2yluRESMZ*pynib2uhUkH72a|gTe5lQjPtTU zkL9#~&TSjAaXFp6o=WG4+3XT7a;9;e9%6+P_Ak`#FO}`TpV~&q`Tm_(!iI{On%lL1 z9ktlplX~{<)}aD>!KH>Sv9T_7(_XG!5qq7-o|>{n}-p~FYJ?j+5U96thH#rH2FoXTjltltv>y@ z23+ipAl{9HF9d)kj7S@ntd6TH)4Y%wxAwhw&E9f(fj)@V$4|^3V6&^K+XsK+bk`dk zjbn%EJ54+h!L@HrW&)YPM3Aq9K;`FO)#hq(8W852khC8S4mas{E}&sU_NXHIp^Nm} zmr#j1z^C&%&BhGa1$4fchhs9B@3Y6w5g$#Z*0 zJe8ji^h-tjT`fKQldNG2*P$zVQY_(q{V1Uu^c6Lih&wR8i}C)ihJIgVWX>_ekVM)} z7wCh$;i2whK|=E7+4|eU84%*B{`J_r+z9_n*_BbDj3Zl zhim=!S9PZcN%LZWT^EJx?2BURErCVnd#Qrh20&e`PmEiuj<;rM*0Hvpo~tL{%dhba zGntZ!9ZwmV*pJgs^mUBX34)ME4jpe~+A;NLU} zQr`YJVjdky`rxxH5}tzcL%p1)N0dvx%no6}#T%NSQlNjU@6Lu#c@Hl^vA(A7BLU<_ z_|m=%DPt!;krqS`tU3GFo{x}-|Ls1e-*uuSbSq?B%fP|H@k|Dj>vv~aLO-8js{g~+ z7Y2poYtXUn=4bx{HoKiic9!uC9q<5Kt?*3Pn&=*W-t^X=R@}L7MUIf+EAwDt3$20T zMwWb@2I7PMiJEdm*m+NybiGt$38@6;sbsUIE@IXEK|nY|FW~K0h82aXRa?1oDMWBc zPpYyH^TDCI0d%KIYiA`G>T0Y9luZVi%p)6c;;xgO(kCg1Nm%KJa^ za=12L%{7FW11~SeM)%9O`kiw<2bj&S3&YMBr$c+=FIbFDZ*kmvL4L|q;>~ABmT>o! zu{6jiJtA#D)RMzFNZ%qIR&(q~`qz#^z6IJeIEHy08|+FNSGt`0<1r%Ts22DEIN`uX zsM*ZrCmi9(=1q2G1F;GF@8%s}pmDq-aQ@lY8yBLUDe+%hjaHHuf^B~8Uo=S15iJC? ze%Yy#AQ5DFaw&^&o|x`o>0vlM-F2^Jin#&a%C??q{RXS-$0vQdrHx0MYo6Mn(eJrV z#w}&W=+m_CpFP`t1$KwV!l|2&ulb%`hNmgG*^eoe{f^z6`;-0coa|LTc9Y`W*X(95 zSIP?RsnZvD96dy)6h?Rm=hk3~I|6fFh;iJi=4z}o85OuC-@sIX80%#LF|5)Uo5ZV)GVHRh0NyiP1#th z`Z*(5i<}p;|G36<-=`&n2zxD~4kJ`Kva77Ulu% ziR{FdXGhqPz}Sa)%xh3c0M0q>LzCFi*H$TQ<-*~XB)uwY%*W7m#|l7TXwD?jN{%0f zy|%a4|J&?!HvdnuGxO!>OIW$trk1q1zSE~)#nr|?NLbPMbVN(${T{Jt%4aQ3a=+^9 zc(xXr0xIbwsegac-DY|9@hqwq&!mhy&cMgz8eL95xNupNEW-L6X%mV^$7K;w4dcgc zD4RVpvcgzPy`b-*KLF{CdO0Rcg*Q-gpmeZ16nqG66(4wCu6X$k!{6g-#<8bwKrdun zPli=6bAObl$cqF`FN3x)(Qcx|o(0zk&TgixJ@8HlE(BM~)RH!O|JwR(>Y8m4gGEm} zu%{6hrKoLk`p-HG3TB|g;qg~%{cfGLVkQNiPbBnt!zjOEXd7<3Yx%ak0eL`=i zm&ASW9N4o^k4-Sb;}toTP>1aVmMlpQZMHT1oGup2qwX42s-FwkreP)awal&(T^=w2 zmq)4=fIt-oXn{b=m3f;l8R4v(gO_Z#ThfAt9D3ko7C6!dN@Ns?K3AnMou;6)sN->= z%ua_>@8HwN8-koe*Jgc5)ZW~9`(Sx?CYrZDQ$qSyvoIrR)^Oy2Vj8}(agoNy0$4zF z8D11`T=rg4y zb`C2XPu98jcgtmRqt5b7YsLhcT@;z(iidD%G&zQ+Vgc|LRyKStl{$n{3_}4}*SS=R zs1krVXs|cqrd~*uCsiR<2y0v+$gCPCt6t*@{(Bw;Sp1XAOSdokkCobx#J_d1m6aoG0IeS;zpQC4F z@>_Z@tT(hGZ;Cp^>y+RCI>Ei2A`v__mh z@buXc&0MoY9VgtDTr!_#272N-nldE0tn=hLBh-CqVkmTB9DR6wfl6^hMYE(E(#SiH zkO+$P18U@>Lcr?3+DTWMhS$4(QT*F&p7N?|^^xQEkS+Wz#ce+U&SBf0mG`~5UEg)Y zdf!JQFI$R?j&(f(_wf2jtWHPy=HlJic$eGEH9YK({f+1q4P>eOcOQFU4N>OcUSQ1Q z{!a>)#xMKn_3u2?aW9muN6_= zXa%Ldgb9B>>Vv60HbYAhS!k7rFyMN1e4xP|oa(!>4@Ig~T~p^M8m&aAMNsgrB@u=g z>$i>yJ4q7IIIo--c1EP{d^>HVv>c=txQAZQcU*ruaxytu@6+znXs7H2zcxObQmZ~5 z44dtCh%X3Dx4b0$?07#$+Mg~Lo#$KRX^iw;Bz+5B_aoxED^?dXd?~XHFSfU5*uLKw zqIrA6M0tyE&hQ?w+od_fai0HvgxO4ptu+qkO%CSYfyc+n#C`*?L&wR#)}nNGpeQJ^ zTeV&!yB(Yy0*0#(^mPgp)%oI_u|NeO2=Q1_N``M=J-l{;>C6dyoCR}aLXcC7po4RP zrb|7{J6+S|Y<2D>Lqb#G(@?%W1s73kYQ8)gvLdU^rfhhHnX$`em?fFNXeVUT{zTHp6^ODJZaSNG zcBW_rv%8oLrD(Ek11?Y`(aPd^D_1RG>0q%V(0x^zc`m8OsiKG{kz92Cp(Mgf0(oF! zc6{)%VGD~uN3`mcgk{CPk&HaF^0$f_jY{>OYJTAW4NcWEfS#9%tm)uua@~}-PbkU& zuf@S&Qrw_STJg2iW)+)j%d12)xr>Q zwaDDl^Hq6(u}+bjcO79&PxH^DHNcPR*Nm>PBPW%o)tI!@o$5t15%lF4j3HFi%eCMc3c$;XNVRfqnks*||+K=ajdiSiaXw zS-wNGN!d|pod5X38nCV%;JSOvX2MxKg3#9@!k_mU@A z6PKl=P}{8TNH*=E8Tb97=jm42%Q_t^nxi6U7!NLt3ma;O2~gmz+b;Oc@KzO3t#@ti^BH!e;2RfpHRg!NNzLc1n4-;mumVqQmd`l&At-_*btueY` z8T<-&B)LczCcZb#x~{|XmYz2xKA->Im!$`qNoJ+BJNob4+b*ng#@VQ2o3+^AxIO>2 zkpm}<`^DY<-lqR|%S5|7_7n9pd6Q1%iOez)y?Pc!6NdLa9JC)F5lwZtH@P@eRqNQy zYz5gLYv>x;8xtBBufwCBwbtsN(Vp&y9sOCZ<^0%J#|)H4{Z0@k4tM?xvjN5E_(`Lm z`zmf8okH1NusM&TQyn^bqxga=$I+vMNyrP4rx^Ofh$z9CNHH&n0JaEacp^C7%x)N! zC#l8*6bh((deDn(pXPj;Ha5rG;Yi-GBV)R4?+)ukvn&0q)?)pBk$C9=Ue?!0zOv_T z-Z}D+#S34hZvtE&HKhb^HJPAIb_>oMyiRwD%H>t9Qx9i%s|WC-`rFW$m-f z#bW`{AtR}z`#f^}?;A-i2R4FHfxUI=K8o{nliTj@?DiPIHf`DoRu79U$k=gS4Qqaiz7){j+low z?ntSU$3G#1pria0R_YmIe2LkXzG*6pfL8xOV}WjEa=c8IU?*g~~r3>0WX>x6W* zSl0y&Q;-@os}9X!8F`lUe3DNTtS$2`x*F=QZf#^Ks%jY!C@$4kYjV{Ydd%al+qRs5 zbb)nog^0~ZJe`6!pN*Z1j7u*(qBSv~hI3bJho(s1sY$jmmP<>}hDFBpj69DS7gD!F zTKYdkokO;z^H#i3+K8`B5aIm_hO+R=)3~Z$i_`bGhh?#Tgcrn9?KHomfJUw4MU&$E zO*Dr70S+B?b!4|*zw^?|__{HHA@~}&h|ueFSH2)wG`zOwIgOI=)#+hi3!q}+wDWDt zsSX7KMMMfICX*e4sb;|7dcih2)Ck&CA_^~PxL0nRF=)l8JyyW5Wo#v-JInI8ClGVt znQ#7p#0`8i-{BAxAkNIr#*EQr6qXu_l;^Xhd0+#NpvR2OA}UMSNC}CjPb#(!yY@e& z^s;iP*dqF3GPd@xm~t@w`%4m}WqlR^`Q-{rHD&1I2$ZvuxJ*hqcIC8c%zVI9P^&fI zEjz;9j=W9wr-g(?V5H)YkwA2$mi2i!V|0}9z4wBW=XC+GsUn9Au0!eJ?j_@XD0ml~ z04bJg6Wc3m{$n2iKXTNm@!V(r_j;ea{(~qkW;uRP{&KE4VEUgN%6z=i#STu^7?tL% z#$%*{%F$uREPMiW+&I6E0lcw@;F)Ame3?Q*pjp(}Pg;4V6{_YOx>WV1Zt<$Bo%!7& zm47V)E`z}tB(p6Qvrm^ekJhmiHx77HdpzSP7YuR5`z!EaNLi<{?T->VAvFHzl6hsL z9H3qJi3F$zQmDh0id&TBQsPLC)97}G4R_pV^&)r>i^DlsTF6dH5GH1YB_y0SJls%r z=WHa7ny6nyt@Iw5&C-x}=PZjMW&a(&nXz z$vZuLj^t$vj;mEaz&O)z9DZ>enT9w$as7_F_wL~ZG%O5rh}30RL~|-tV-~qorTh`3 zlw@OwWJ5`L6FqVhr_>gf?VrT^lu%FoQ$s6z~)W@CyzM%+n&1;jT@tz_4-&=!mZ4gU_REi8&ky}`46~!}8 zPSn#+EsF2bVH+g7Zm^&x*Xj3agIa*HOL>4K--c>Xhx-QVB)cI4I z#7eS-sS+>x;9i&ix@>~$NTdh%YWNg|KeHk!{gbACoqk}E5kj|r#NL@siEt9mobMfK83uPWm4 z87eLY$;B0J8LeB_Ebdx9VB^IpDbBX7?)?O~c2fQR04q<44)A|{AzIu^M>EnXAhq*H zrI77+z~9pU`r73P%dE}*K|kQ?^ONosvkl@#kxk4WZxUhN&t#n|^dLP2ahG!=SV)ae zNzXjI&YsOGU~q^0nCFU}%W`0W#G$Z1t$1(}f5Xc4<&oNB7OMg>A=EhJ@Pr*^Ime%+ zyX7btrEqe?aOg#Q?z0*V=`3N`ozxwJYbdBVRUFkF;0wr9eVrkGrG*o;Wj?tVJ91VP zt4Nb!lE|5Lb3XsF5jI|l;qAqCfa76vy873Z%GU}<7n}JxZuhSFS2L8&h=t_+ zFBo0g`>vkGAhshID?8o#1fItMoEP8A$c@{iT@&cvoP2(g%97^DE+<`$KxdZ-3AYyM zbTSfI+Z!UxvYG8O5htZg$_U6^fUuQ4b_oAVt=b!q3OMe$rw2pwR)4fhU=!H>Rooo*V3L1(kTZ~by$HFn(dq{gdM=*)2s0L9p8av zkG$$0<0+LCmNa+lNGy>gEX^6Ma5`AS35C0K8M2PC>&A^MtJF+5UQ-_T49a@?_({qY zrzWqAFb}mtNoJ8|s!h3LsN)G+OC?X{k0f26NOvqda|26SYmK|nK=7NC(=zDG*7}D< z&1LudPRf}4V~Dqf(&Bg^CQW(hG#!9NN+pc3c>miE+J4opI}YeQw4sY3Zlqx9zQp`) z1k<;xB3@QP>6%ZxE$4dVt!ECu(#ytiFVeV+NUNMvI1fdK#i*9B3G$B6abaC(DZC7v z&-(?)xM$i`g!LpnRlk{6!JyD5{aJ?*-`2J-ff?cA&)>Dnye@CI82RgDRc=4Mp_HmJ z%$@i96LatnH(Z_)ro|+6mVED>@v#HCsuXkF_eW73`MIDxuUD_w;|onPpZoa}h&7DJ zDM*EazCVTyx|#pZbSM~t<_NH(oeogHFu{VF8kG}6%c?j^INsZ0x3F+?n043c<4+#| zU)$f>P0jBL5G8^|w%ZL`3XgOWL%B;JvFg8mdglJ3wvxe~Wm$0C4w&9=DCo>orzP~Q zriBanQD!R+L+VO~%z1#K9A`Txm|hW?)bkrr<0E9YL+Hg_X2nT@7ebTJIF*-(3p zZmjnC_i3B|Pd@n{(tuV0X;7Iw8zZNDv}P+q&IBiwWCu>%51N`OQKHG=qX54dDEez0 zV~mM%oM@0_x5$r>YOqB5c)Aiat%l(^T1>Cz-wdt^W%LRHDJ%$H*Xz2TsMUQL>1jN# zVviHIFJ(cNl@}9d2BO=^B4;~petZ&Xm*L$q?cHUN!CPvSyrm}xkKh07Z}xrr&o^p@ zJ-lJUYhQjktK@fgodD9Bt2}z&o4bbZY8^Q9?zQPu%y|m@|Pank36N)h?Vj5xzMy<8EDs>zI@GY;ifL<8m-a&oRIv zJ;%T=xNsOz5}cq)0bi=5kd$za!6I@D5>-`cTvT_Ls*;hKUTfVk$ABZLq&EK4P?2NE z^n22h6ZLDXAfCqSIR??Yr0aGu*TK4ddV!FeLt}mE82cxJA}3*ZCzY5`0x(XO8Y6v8 zh|MZWouiwZjCylZYAOcukm^tMXLv+jEXI&xOhH#pqnbHM?3b(KzH^qqozdlg1Ggvr zKf-;$K*%kj`fP6+;%Y~3Hc&*36KKb-X}n#qBX&~<>|Im4W?qGMOEiAD6aFSU;aSKC z=JpOUzD?9>+-*p-sS{eWj+P@0=H=$_OFFND6l3_O(JA{#r&;)xd&4;lelpcPloQTj zpmWJDQRPaNiekmsaNCK(E0tngHk%U8H?Ba(@-GOF`@buqAl`ZTdL3dofAJF#odP1x z?*W8&`il7-VDIASyioT@?n03%{y>n8k*=mFcy`6k(?V)E7QFl^!d#*AISOWzfSD0W z<59eRG}!@=Pb7fUblrCry&I}moDcK}b#wEgl#=A6M1Bn=Dnt{6h$!%;wNcTUFWZ;P zqqWRHQM`!J?5;TC%^>2^B6m?HMsSh4LHU^hun~hNK6?AfhRx4B!TxsnJNDlopLlPO zp|tt425O%-W$yI5X3TF=+y#Mc1BX7erg1r2`33ue9R&O7FTplmUN`5FXIdMl-naCz zhaXvwYoqsoS;g9{6_i)%UIN<8{ks0{8Say?0Ke%~H-Bc7Gh;R3cm7_pnIEy;GuLRn2_?AWyJltjy`C;9Nr~~f?p)D}qo-CP`)GC4KCaUB*KY`q9Z`qy*pc6M zgmE73Uf$$;)z+Kj7l7 zCsq^*!SmLVYs1b;&T@!p^8`y9Y-=ajZz1gKL#RY$Iif|3=o*L;8OzmSrzH2t%|X`l zla1v3lze|U!_tOB?u4VsBKEv~pB+ZN*J23nEx$jUUy;ZdazZYa59&3%{EjMK+)Q|G zhNw}utqpIlA|@m$!D+Wz463*UK+`W!R|Kk{inh4jfWmQaYIbqz%W9 zpBp-);>JN$6_Pw;Smh0aDl7E<)Vj+%^zP8f0U=mFO*mFHm-Z7maZvV z%{#g7zoTe%??+lLIiO$8fO%8lJqvp$vvA%Nn#bF^awkr1cm|xjv#VFt)R9lKOZ9`{ zxO>C%m3>)$>qsNMtk*KkTtMrYy;^P70yTo@%PQp)Iynn=Q3h$Sz)5Le*b7;1aTmulay`Z{s+?7P7`-OqNZrdzGWaofN2XmiDh_eGG)ny=!nqd)FmtI`qEh*sJ$F;|Ot2mo`FqkHix%1Vbhd8sv1oNpb7AQF=1?QM0C~ zH7Ml#J}cfj<%|TK9lV;{P9w$LPU3y|Xu9)5Ng{~kit8mM1eG$z^-kHmHXF{qFZl4Q)s5yEbmwvVP#aOz&c&8GZ?qVG1m=8uep$>77ge zI{%}~EDj3-3UQw085}6rQ#gGhi##=W$dhR^LwZ>~J7f*S$q4Kp$liJ$DzpB662z%*l=hII= z42Bm`1agNDdxqZ!Vpy=OYj>WwxIWx5zIWE#>CKV)5t&7u@%9a$X4v&JUj5iXT*S;T zE|uik=sTx)$Yi(MHBnOq1YIZgH8Uco5Kf^i_PE0ib|mFkfj`(sFq!ztT%kfdr} zUXR)Z+%9S4uZC4T`Oa&lFfr|^!SaVUS6BWb`L!9n{xB$6=uH?YACt<}?V`@mqxVng z!512U;bBKiA~#&6+E9y%xTNw&X3ThS$;{gxeYUV`*TSAXyA~=3r`~_>ZBrNCKRGuT z%+2l9ORwcTEFY6Csui*2hPsOT4#N?n0+GAuc=xW;9v2&9HmI`1@1fT81~;!LwWfSg zgFI)|ox-8C;+U1@<#%QeA6D)Y?^oQx-zy~rg)7#30_nZP4^O8%|4GMd{r?}ntAZWU zR=VbA{T_iTsSb90_F3dP?PouywLh0A?Sb{;KCUjIWC-8;*8XcIcu5h__;pr}K%u=T zNVR}9eqzD#60fu;z7`xa*>_)cfTQYg+A3Asf6E2GBAS;r>sLg>Dr^2d$FEOQcE;~# zpF!4p|0}A@1$d4 z8lz}!$H8k{5eL6z0Q5`Vpi&7kL*1Hqcv=iN^bMCc$;o@0nIsIPQO-#hj`!K8^^UDy>`%;zm->txFR&-5eHk<8c zyZF@#{Ju=D%Uj?nfS~x*3Pt?4Q_%05&$5NE@JusXsTvDn7toVWKDmYtY<+M2=+X1`JyyRRLO~rGfIv+6GAx%zb8+7!Ucc)(g9N+J$;_CwjfcCR0Q{ax~*We;rg_V8@~SMg=i2TZ58 zy8{K=zJ(B$WSSiAX~O|rU`o}ztMu55ji+NL8PjxY+WwFj)8+j_43K811e zxUgR>oN)c(P3~9oC_x@~X)S-DFTn2-OFBO^ST6M^y;q{G~mE9b6t`ZPTER52e7I^B+@M&|1gG4oY# zP*Wo_HSyFXpC(Uz>GL#LJI*sMKyKvoqO~|Ep3v?jJ>dlGlqws&)b_JB{$Cc#~@_zyK<12Ll0C?JCU}Rum zV3eFS*=-wVJipCX26+w!5IB2P;vS6tSN>0ggO9zKfsuiOfe9oE0AQ93W_a3TU}Rw6 z=>6LOBp3WE|5wSu#{d*T0q+5m+y<@y0C?JMlTT<9K^Vo~&c6*MNDc)FQi_O3kQ$^& z5eb3dAp|KBN)QR9NRTLa2qK}B9(sr%BBAtFp)5hvlX@y^>DeM4L_|d5tp_i`gNTQs zS>LzWLeL(5yxDK&o1J}cM-6Z}1;9)KN~qwT-b2Tp#f(|UHU9#N4ydY==%{V#HVUSW zqRgo(ifRJ|Rc6mTj!nxrI7EMd^Jj3=b^yDC&}PxL1B7OU zH2C}uZ8wcjJr$y+y~=tAq5lw}TO*5H?-DI@u8Bp{L(Zk~!p;KzF88hRJBOr)^W3M) zGpDJuri7HPM88enyJ9|}W-|!P6zbHv*+E@rk>k6ZEg?`XY^YYWYJSDz!0#iFy7?Ke z52Q!;5a-uH1(PPggpBn!%;__jHcfAjT8+I-yyv(}q}C!XUbBzeJlk>i z91Wd8-VBl+dM`DD=s@4$S;fZ`^5l|y3w;P|0WI;{dlL0ouj>=IDE)pK=Mt{d`$Fvd z5%^nFW)bHw;-x4vcth`=Q3LXaS>+FN_!pjQEgmzAaU=`L%)X+3^!+IO8g*)v!#K>~ zG5ues-Y5I9|49!2A^+HDesdhjBF>r`XZaRw|0CDSKhnpJ+42^s@AYf?aF@9ys#XB+ zD=Cb?cj_wj7U$$XBpBWs-mR*)i>#m)P}E&y1#_BXg&XcOvth6L!MjDgiD6szW>#sr zD|U#CS>ib#ASa}P5j;2k0_XDC9(dYgU|`UJ!YGC&hC7TdjL(>Im^zr&F~(9Lo-tU#vc?D_GC58L>@ZJHqydU4-3%J%W85hZRQ&#}Q60P8-e) z&OXjtTr6C2Tz*_NTywbYaSL$=aJO+^;1S`;;OXGm!}E;SfH#4+gLez>72Xeg0(@qC z0emHVFZjdwX9#Er)ClYoED&5JctuD|C`2er=z*}6aE0(Qkt&e~q6VTRqF2P2#Dc_{ z#14tQ6E_hL6JH?yMEr?_fJBSLHAw@>BFRNkd{Pcl2c#{elcXD@=g0)fprnE!pjk1)o zi*lawEad|#Oez*CDJm0G_NjbO6;riRouPV6^^2N{nx9&g+7@*)^%?5FG!itX&upK(st6W(O#l`M*EwNgievpGhHEF2i-i~1-i%d`1JDhZs6xQ7{QIX)xJja>Y~v2#rjAOf!IR zk(q#5joBo#59TiBJ1i6|bO5tMjI#g$00031008d*K>!5+J^%#(0swjdhX8H>00BDz zGXMkt0eIS-Q@c*XKoA_q;U!)Y1wx3z1qB5$CIJc2@kkITf&v5$jpKw6NHDUE5L6VD zd1Hxh4{-(;JG51Z9PHA5h8U~#)OqR(aUi}jbwoyn(#dyP5ei)}v&O0-?@#`| zh(+Ck-k-3~NVsL{pf%5!9dypE`|Q>ICA2PMj_XpEOMiQGU}9ZC4Kn{5m$27! z>8c_#uac|h?@G=Fr&E+}D$gD~s*DO!)ey#f}mn$__ z>8-crjAU}Am#%Ui&|BgSt8)_bg0xlDz9rQ=T#Mq%^6VU!(hIHsCie+l z9H@l=0C?JM&{b^HaS*`q?`>V%xx3>||Npk@hPSN6-JQW!fw7H_0>cTefspV9!Crvi z8uS4OZox_58HWep6}t7u8~5_bU2>PZBZ`*zt-O6H6TNB#=lF z$)u1<8tG(^Nfz1UkV_u<6i`SJ#gtG=D_YZrwzQ)?9q33WI@5)&bfY^KG<2-kuv3PE zaw_OSPkPatKJ=v@PF(b-5;qsKztm7)X`M`R%vxPkz=8(j&nYXNAml(ywHZil28@!iT_Hu+@{Ny(WIL2LW zbDUYsW(U>Wr-nP+<1r6-$Rj?6zxRwMJmmyFez235Jm&>|KJ%4L%pt&B=21%>`>1C= z4FqW29mJ%s7`f8gR{F*6L z7qD0?l@Xm5rOI8p(yFv8E1K2AjY>_aE3HbK(ylC1I+W$gfAgFXH8oe$;=BQ0C|FZn z)##6ubWcRP(qS{WL&5sy#I5%6xFY+6)s7ufE&OT;PRhH2VnIddj2OM1V{s10Zss$|FTK|umAE+ z00+SP{}^I`{(owZ|5OhDDgL*L8^H13xaY^Wba0tuzK3D; z0ErQCzXZeM3TYlbE0TB5=(wu9TEA0F0kV#_O-WHCYTINIaR<$uwQZ0Nxpu)}8+Xo# zK351TFF*2;cWszI0}81#x8Q>{OVh4Si;T2Wv^e2w`sPYKj03-h9dWHnKQyvJen3)F zQ~t5j^`_lSa&+Yq%P4F5DN_8OQT(#@Wew<6RLxDriBt+yG!hL5f7G$dP_2E^!85s{ za-U*IG14NkRvK^dm}bzHW9EgVAg}x$aS{7xe8i zxe7lK)YqKme+>x>K!5r~Qe!D}VTJ_@BO`_h{)KQg4DM8fEUL|RDj1I%u|g%wDCb;$ zUUJN~PePEveHKOjdVJRo^@_-DANoF$_W{}Tb$k|#8<)F8J*nLGDr_Ot7<_~!`Uoln z2)7B;!;APxn4v>PBdeH-_)z-6$Ndp zcG5TnXz3?T(fA#+%(LQ7(dR44wb#cP5jGD}$9XcJsEDsbDPb%(rCSXfa9(cKZ}NUNM!cMtquo3vqA5mV)*Yq^kfT~Z|~ClbvjoKOd#GZ z&ai0seQDaME7-YPDqXASvNO)1aq34?P0vLe`h+OLucG_+j6!ML%sj|P!uO;F&u3j~ zy~*#K^AjF-_x&ilh`aSp2eR#$tE)ySL9RNfy{fZ+g=T#13$MF^i?z{&sga=(F)T`{ z>Z!3TO2#U9lk}6E_~D55v~nbuk9`hA!$X-V^o>93wsrsPf43t@C(lifQI1ejP9Gl{ z3X+E*zT)~GVt%dglSn&yNsS4T-u1RwfIWiokR7gB#RZpC4SXPM<`At zRNpRJV^hs4vS3Td3xZLK6e@h!(EcbyZfZCyWF{(tpEZmO@_k?*E5=7TLOf@g zq3G9kDdYLqP!PJ@B-NRR!8D**rY`O4J!V+^Z>)i)%cPpGrQ=@T-Z)dZy;3K+HTgpl z&7Fp3*$y<=?mx1F7TIZ**`+nvwb$4^oH#%_X$@0lmn*QmZ7ZRpiNc4$z@wDJKFo_> zjIpXJZhPqboJ73)t~+u;!=o9QEa%{9-%inEZw6KVtM)`HuOMxLI#`W%FuM1cmMA zF@Mz=Chin#OFa60HnMn&6IKa_+r+u&;kwI5N5B+_s-N5$c@OTQO7j~OaTN+WJe{d~{Q zAZYbleP*?JjIn&l=rLET33_DibdFnC|0i{r+|AdL&05D9tq|cDSxU8sMn)Mc={Q>R zu0%|cJS=%#j#gLTBhM$`nIgCz*LR_q?~BI09k#xEPNuc@Y7t`EU!XV+{LN72=jr9b z{nt4eR-BM`5)zn8a|G|a0-AKi(a+Ub@YXcx2Q$Sk9y^*vSx5R2&{0ME??+WqE11*0 z9k|F6Ns)A<1%spcm1SsqE5Cp|g|KmTD@o{xu9u>gfD~c|iP!cp7!Cb6l*Hh$Y?pSY z2Ld=3q#|ck4PX|&W3ZwQzz@0)Ez}fZ?eVy9AriS;p%6J3W~n*QpPyLB=Bu}fDpZbN zfpqQ26=}wVW=r5oOgN=0<)FGv$aG;3l-DktOWGT4{NZ4O46#ksO z-rMS7!+@TtHojltg?9NC2b%_`dmOTLUs>Vn_ST;+d`hLKO3Jcs${5F@0rEx&p>2Q3 zKKhNBDq$T3gOrR#v6@cgjMnpgD9W*lgaw3(NHN<9E zO8Yq!9^%*cU;`LEfWSYY$e=K&lGyQ-NR^qh=wpnNCmHhW3gIQaM~Ue7G;C+NEpzY7 zRNzD3+x>=3jCm1LO16SO{<9oPwVP1&$?sn4XAF|(Q)E>P3Nq~^DE3&C#33SA=Posx z_9;!B#%(N#SKg~uX=+Ui(}=l)SFshb0`Ewc$y=(lFE?)Q*@C3-8VRn_*K(vy5H^4; zwoTGN912$G>xR2^=Nx^bECevueQ1;+Hvq8^Ak%Q+#e^SUoNGaxU2S|Pru#B&1k*iR z*XfdUD+Cwgs7<{qMmk!Ui%|{kDau_V=n~7`zT^|-v41BFT4)HQI}#Ty`EnIefH-~& zPzYDc#VhY(qG8L%PJrg=Vs9)o?<3U60)NCfYp*Y|*$lVM{P>YILeKa7;mkpdtOJE% zhQY?yUYL*_*d`(%wI)Yd*TcfSL^J_p0cd9O=%w?`bu`3W3baZSs39`XEiRH2RiWaW zQe;oGNUP3H;@|I$I{{67(ZdTv)#D5ZOAz94{0odOpc@3qj{V3L9mpwM{7@QA0!UN zaYW9Fbwjz8^|M}~cLpf|G1kzp!iO+afWPxwf@ktXSR7!cNd4(-)1aThWd}Dyb;_6Y)$eD}Z!Lis)%1#Fr z7K4r#KJa51W#NHOxbp-&nYZ+%dg^EN5je42Qtv)Ns(77v8o^BVy-g|dRrLrSwPvkn ztxW#=ubRJQ6HjqlKASn3%>cX*tMnH#{y~{}PZVkXEjK)2*p8(=_Nx z#becxK;YMmKj`LvsY5v`1IT8Ynh8){>}o%;vT2MC^H1%1Mp@W@K7IO7Vz^=L61GWMLK=gPB5ogyt-qySy8*Fv zGTZEu6^IhWh)$#1;Cc3kTj_Z1jb#g@1UM*2Yck_+D2_nnvF{Ohe@(zIlQfVYiAr*6 zWOk>X^zekQ(**kPfMG2cW-`^a;24T(CkmT-mslQ6_#+ZKdtQ8znIq?iZyXwlWtT8? zOGnr)RyCNKRrkakhcDgPDZK8_)uhn4jBdD&*wNQmEO0-YA{e=Q3m5A6!u+!nigBQ`@7jBs6e zp*i~_sOD$C0p{yc0-uVtrDIf))Qdyr>3*EBB@sLigUb8}`_SC}`d-0@C!6~<%WND_D6|BHm>Ke>@OE@yOrKR_=7dJ7+Prg9FP3UMwrnH=M+!EJTIkNS zf~a_bbpn87Zj#;111TdA!)d?>a3{UkS@u9tHFO~#(+sv+Df+eqEi$EHW7_)kP}1z| zbo=?wL)w-3*&%j67v@jg`oZuO1Sw3&3*0m(a;Z640PvCZn0JhJOeUNzuy?%xEVgC( z(`U{U$!}NY?iTKxtbrtDw}`ic2ji~aP9~>rHA6e9#XZ7Rq?&BZT4(gHWUQE$&Lt)N zdAUTaC=0@Mu$sZ0KDt1)VmcanBy=zDn#axv%VykIlI>i9yiKBMm-v#Ga?1)}~*7+2gSOdQaWBCN3tJ&k-T(A{2b z9vA_F%>g-;kEItbq`?`3!J@VuBo0an{Ja6KZ#&9kDZYEn^moi$L*Ed?&9l{T&;-i! zilaIV%{@8y4kCPDY#Gt=@gH@x@9g_?0=s^8oZScA#CckOpL}@?$KmJ~ zRa^)@uG1`oE)Yi_Tv)$Zy3xje|0P;2h>2A83*dXy9ik&X3P}6)h5q}3@|fYc@f3|= zjMfsA#yLLs_k-%ghuoyY8Or-#$wnS*D;IcYn)bU0t{tePlfCeN`t_3v#6-d9_n)OE zp)N6u&9+eIm4~j4;-gT_7>lz6szlQ{$qe8CJYzS&nCaU<;#LAT?$KvzL?dL&cHu4> z_^@C{d>OSoN1$x5JD1Mhm3fhR!`rMa7a9SnmJ$(cJWTER7}2T6VIXm7EKne<`D1(t znHGHwHMjH@^Y2}Ay5mFU+(K1&x^csgB(cTnau$C_2yLi6&>&))A<$V(Y56z~i-ssF zb{&oPmXOY(sk!G=J_SVmJ%}rXEXzijl@=}3UBEAcx@m#WH2=&{BPh$EUMdF+mQ=#Q zRV&eJK-uG}sI@L6paV;uhn`w;O^h%Wq7zV&sjopFGiBYVnlp^1DwW->aecPRd8k$W zduGf~++;`yjko4LNYNT5Ae%E=5$}4 z8l|hIHp!yYO7u7Uz6@m+TFJ|;pzN?GWc`5Y7WEx>MHe+yjh{_>MPq=98tO4@>4F;9 z0bAs$n`1Ze#PuFrJ)u5we(y^jLns)TC23PTL3BddyMvV~+e*7erxg#AYz84D;pyGrkT6T zS;#tub~f9DBh3w2vwv(|32_a`FcZ7vr<##|JAw}H5N4ra>fS)&Y$WR=wP<2uao)0i zib|6 zfr62&nW+zo(q{^vgyxRSEB=u(IHP$|yQHsdUrU;+*^<+3X1Cto3doJQjg1RgKZT_+ zPR>WRtqm+$*j!EoswYv6%hJq|MO)>q$YRhdO$Hf~G0qY|3F@;AnJBTyUGScQIi<}X z6->Le{E%OaUIW-PdN{KI0B0t0tNl%Kc|&7ndsN)rd%+?OsztRt2 zU$eK&8UtU!BL*T@s1A>8slKhS7YhDzKB1edY#phVKsMER-DoU@73h13>lC#_Ub}rWuzV&ijCAj5CR+i;|W*t#v&47fTw}FWh8G# zJmDysau2egF# z?8}QHv(_nw&aFsRKY&l!##vq;{*0=|T6yMdb!${h;S*o*YeIQ|k5T$}hAXaG9}EKy z;kKe7y`}+Jg5bX)qFDHdQByc6W9?%w}{O7=%g=R z)^O=cM)huK(SN|?V8J^FtM9GE{ZZ;l#kxXdO}9;&h<3B)y(vgIRzK7O>M@>uKZI}( z(Xnbgxb?{zA6wyaXVL^Y_dyL#jT>9(b8Ta6^Y`Ph7fF1$%6(#Jb<`z=RO-h=F8A4u zx%^0z2g)I6d&26D-g7X1OVzmjlvaFWIxL`26Y?Yq7yX$gjEWjr?j4q#JF7jpi3Fy!V>L_)F4R|z4nO? zH3zXD-J{eOWsd=u=wD~d>;gH`L9gL^NYKOn{k%h4+|b|pr1@Wyb3(9lvA9D;jwTD` zaG=2^q$KDt&7^Bwbo?Ob#@sQhGV2e}nwbBWPYPnb7L?Q#GeLBkMFOc*^E zZq;^ZvFg|0Qi6sOeUP6#O>-ewV#r5!#C>am=h=E<>e7Ty*|II$NDcyY*wv9-t2zr{VOP4`mT6aSNY)_R?_eI*y;5`jLlx$bI+QH42tL;8G6% zJxk_O9bRFXfWUXOJ}Vc5|Ju6fn#93cb-2I2L1hJKlYA!~Z9`N&*&Vh}=e!__u^Yja zo~j~)3gI=hLt4H|Ank$A0FL~S1kOO%0;t0Gli`|kC=-jm$|e4#cyY74oqy;2-p4W4 z{T_PMjYJ~Q#Y3aafS`@enS?afYql8)eTIx_yd0k*HaNK*)V^0;PrhV5mK{2*3=@GahsF3AtAKi; z)&BMO++|4iQDCtswDy>X7j0KMAlZ?|JgSgff_6>+pOM@4*2ZWqZQ$nIKTqsI$-Q2# z*jp=BMZBDOx04jbw`*->tWSSJlv7YsyRr zFwKaYj1K&uG+g|u1KU&;6}oh1#t4E&f9!>`CjnU#DXVNWVf7QOymx9?GOcK?wRUro zu(=V9%TzoWxv-gPeA%i8mp91>>r=L=W3vc`qH z;{yXTBjx1scd0PC(m;$Vo~4;c-BvGbkBq2ZqvG3kquBb7Hh&v7%sg=Dw$M@pU z9QsrIJv6%!=prWn5Rl)&5E^a7sZ?t&r!dhIa)(o)&wn ztqCegFx;>lp%R)Fi%itR#q#~+Q2-B$dDgyfkA1}tvKI;8w2}`MrVIxqh84M=$&Qx! zEFBYUP!B3vM=|-x6r-8+0=xk?)RS2XeqW?NWaPP|u14%grvQzl@u$?F{xIE~=Z_U? zVb6=#_z!ifp45Qi27GTdr;^@@T;RKi-fPuiw72 zSXaZ98WK3})&FA=Q2ZTpXl`CWT07_bhq6GGY-5SVl&ZhL?1^qzxCiW`(o3$!g5}%;6V!w zX=Xs8ei;fchqO3_qbHQO`%e}KPBi*iY9BV)k;qWok9<4I2D4zG7S+aK6g-WS^kw9F zehA^u1Y8JU=IM|8OW0qfRo#elmB*5kieoOXXSlBM4nL&t$7<1X!D$3?vzs@k8V}BSD7dfv%^EBTCI!N3-zqQ?p}+xFb0!>NjN-&C^bRlbdah+k1jgk-RJ5;)YFP5BFni4 zQquq0O>N?Xn?EF(i-LAhBRHV4h|<%ZC32^)i;bEd2A1v;==?O> ztnH24e$o%UE7B!FGWv`Y*WAhN5x^i{7at_SLe%-FLYT=)5@_BX8Db{IomC3zAghW0 z;2e_#*Y?nHtJSd`dg+2MJ4Z@L(#<&ynC*3yPg%vch|O`d$Tv@yex1WpH%Di=UpCN4KBuoLWr^X{f z0G_x8mDdf(Rw(;X7|N6N3e0sVPnom5ZYY!@u1P&3OVuhExD&bK{w_|u(+U?2)9JmN zVBZxRRvTho?tZ`h_h6c$JcP_jU}y(VH*BASLbFlSpqbN2dh{Ik``Z3>qs7FSgaLG7 zeE|Vl>o-O3X294vz%rT4YLq+5qEmk@d1e1~;}_1WMKSonVf@W3{$NjafB?NUG*6ja zv&Cl}*V400&(t7l#!Q{i1=Yfxc#i(h({FrtY9sE<9~XNNP5DWOwk@5S!Te~ySY1;> zeqyB1C(*J|(+1pS#Hu|e_i~~@AvUpDFzVz;vO1a+hwq3*`$5QNZCFO=El>BVu`m;7 z^`x#89tlrL%>M0rt0YDIlKL{AtxmHs78g(k2ID|BG$For+REvxww3_K%X?%UabYD} zF|xPnw=cNb7S#ST5u9q{=Sk}+um=JAYXl>GX|j?;^UlG4a@{wGkW4dTA_6^Jp?+vE z%?Z0??@B;N8%L-fnS&0xLia+qn`$bw-J>xa{M(H{wuc+!hGjwpx_homQ5Dlz@Z!cc zv}$V1>QM}{nPWs!wF}tb(fcm9Qrc9xn}56M5CBcxdLdl5Q^f47-b5ZHHUs|2b0_m4 z0gcMp0KZcbmL8rF(a>GbKv}auWy)SDSzWUwnTlYO8xl#A;YqE{H__SVo zz0`>R=05p8Qbgu*I{7EKPV=1y9s!odIK15H&rTHCwPX5U0GDN5h zOAo*!=cj_+t&q}OjMU+ayiARJ*^3=1CpaTDA%a=Y=&D?#cOspMlDKa7s8^`S$>4}I z_2JWY!d6UOCr+C&0zg1;hoa#j+A`55207p$yy;ZDtF>hH65r^Jx)-E@`J)gGu6`l) z&BgZ!TLssxUjC!y^`#^eD>+jIH)C*i3m^P@R*0&ci8;#Q0e5Cb>C#oal3v>{2D;oy z)4Q~)IAA}v$Ky0o3r;*Fe1Q92bhT&hp}kX70U1>J?G1pjx(Eiuk)$l#tb zx01ZDyl^l{{3XiRPdnfo>;%Lj<^ zbc9rj2qjDg1zvI};j((E20nRzD11>Lzbs)EbZLHhvE63&zJDBU~6Xa&Wh0#}-ToaHi}7}Bo3a#s@R zfKI`FX8LDCK6SPquUu{UN~gh|b~<(018R|<&evi;=9N7Pp+G_>YY`~^Xu(X-$PymH zneQCEtb&v==X|W~L?kv%sikb$#Woyxej?){VY}!V%za^wLG_%}xiwBSy;UYVu30V# z2w+FlT~JCiz4jrn3q@Z|?C4MB=8AFb#L*w{@O4Q>&m2@|CjY)u`+_BTA{MI}2krT1 z2oDo_*4VV7dEh2wWJ{Q4)MJ1LKmLdu^Nc~)5*c`lgU;i-N0EXBwInQQUHc;Q3I*2Y zmngG8Y7(-2fgfe3Pryj&6E%H2K63Erk(>d_d13>`6{`ytgOExh+F)2v@<7r-7P!X>gORv(U?9_(8W@`Y2U19 z1xAoco9KPfV@Oy37paH2sGfXsyUr_&yMs)38(c>kg=B=c?Y(?UUQy&4bUChIkkMd) zDCjHy0p-WEh%u%(eFZTeP>t)|dK-Fe)Z9tU2YyKWGp!VAiy%Jv!2UgD^X^H^5!q2C zH4P$JA$p67mXLOhW1G0NfV$qDG_@r>B?62-TiN8uM@4rjAC1&*<7Q11DR(WN8WRnf zO=r*slqK7wcDzJXhYe6SWre#EACyek*9|V|q9nx$-|<>5%Wo?mIzjmDeswP2&p6@| z@wHUU-pV{g=T3)2hB)W3wjY1>PMXLht)h_>-n5JfIoeQ?IK?;;nl(vDCpOelMCRHb z&qy(PB!EWJ{me`}Dr3NGO=8|Z;TLIO756O@xdK`vWlOugX=vsC2bAu^PO%WzvS;^G3GqIFGBQzeu}A_#V*fF@kP z%9YxC45E|>aQ6z+Km62F1<0wIHhu%v7y3;h)cmTlw4R+{y;F%Yh4ttnm8U_sbv~a; zCcvN2(#=uVjKK8veTjOG>S5wQfZ@rR(1U9UF)ZVS10PwindU8DxZBE%%u(zyG-QG) z0u4%GBgAYY%!9G}etyZF*t?8c!>86(zLc}udk^*T)49i_Wf@VDWVuz|Xrbu<^0v!n zi6H(h6RGSX6$Xpy@RYa=UcJ}T2vPb0yKaVacyq+x%mG{gcs!T4xSW~oFJ@=Q=h>7l zw*|6g11FX;l|d?1fpu9%#aCTtC-K>)TnI=hXt|jQFwNQ1*Efh8CGFUwBg3Nc^XUpt zvCfT|maJ}mY5K#zLB&{zs*JxX8>9J~E*|a#u6ba_-=!8H9lka3q?X;+%#9icL}E*^ z5}xCgK1tjf0K*2}7`p3q??#U=Yw@Vu1Oe5Ra%puAy2=FAbi#JY48D?5(STk8thJeykzRyV3)P-|!xKjBEln5x<3Q^Z~Ef`{^5z zTG%1e=7<|<=ebv2&%6jCIqA=e2wMttHbe;D4?K)B{bfaioR)~455ADx;d4*VMW=y1 z2WpM!wuZJ7tFwwWM)ig>Z`?>5t%k4s~QOWU; z!jL_8sHWF6iXMxNM0?|bABK<_J14;A>7HaJ@P3j zm!}zDWIN`UIa5K0p_yzCy}}-AkM;K_0Zelsv#2>DrkH?4I!p{@7OAt`k@0CHs=C7^YM&YsEi9YPu@Rd~? zlJ?2Lkd1h8le4Kv36Py06g7X)n&DTNz3rtJVPY(?zHbcL#nI!K{3Uwy2lt%w+XZsr zHUh6}N}7V0z;s-Tx?*y8gJ&bP4(JWd&^dtJ5F7UIOA?FboCkjT}<@B^!FeCw|)>3Y$s9q%i4Y>iS1pg*~?9TGanZcch{nkE%+xTct*9BB7q7ajLdqqLC=WD!4+ttCf`~ba^-U`j_diD#<0xTOgt}HR{D)a#|uyYFZ%pcTmxhtmi1QpL=c6{mK zgQ{0sVt__enH+BCAiGw;*X#&z1i$ix%T6p31A^|+5Q?=3?{CW^-a;;5$)O_KVnODo z>NYAi8DTJWy~RNsf%E$f@GoLc*?!B2lEsuA6wsP8&n1WHU5cb_T5EB zRAg*^8_$UwMjt;On@son$Q$n|xEPcDryh-2d$<{`Zeccx^Fu#_=DmE7ESlK#V;8=6 zy57~V7|D-u#gPHuxJF8uFWb_Ar&PdX9mB7?@E~o;>O~P&_D>$APjcAj2Zkhb(`kID z0vdhiO2%PXzkO00u=HY3l?nQp{Qw?%UGMdrJ-B`?^VAw!*{p!rkCB6A9ctR zb1#dDBe_T23W44Z)W9P`&hPt0P4_=NQHuKI%Pf<>%87rgk$TQ25WWPCxd_3Gcb-0| z?!s~_MO^S9V3fQCA0 zV?-~PdN0I^SXQ@8i~FMb!`rXZB@&T);xWaDirCm3MOG3`?qInr69o-Bu=h0oOK9zd z!dbet#DHmb(zIs=NRJM`Q>1Uv$?rTy3W=DorFAIEdPC-W;subH+s=-8FZCbU?6Y5QQeTPOV1ZsrLoNLXH79!C5;p{t z=T&g0dN}a(FL`&@{~Rhwi@GkdM|Ve1PVZFyOmVluGYHR=ICcfq#iRf9J6A~W|KQ{b zi1_eE+WhS&{Z*;H+TM7rYa+%LuIfwvYXXfd77LX*uSTI*rZZNDQ|Zx=G9@bSRQ>$SM=uG>j2Oo8BSl zLHvUXNSy@%WBG@U)9fg2fw`{9us!HfnV=Wou^uM+oEXY|Y* zEDuCce@p#S(wZY82nYYfMK@Yo)D+x5(Qg^Zh7^P^Zh(Da*%f}Da9dGbRL_-@{0(#r z!ZZwDm;SL|Fy~I5?)BG>LKqB%E|5k3a?`|*Zc<~lhm@n@>Q1%OH1{PC9VNfr~tGXxu4I5uj zq-6S>J0;{qE61S8HT|Ty+3;?qT9bA?DqOZ={g*M?i@|L1YpHtv! zpwCJa88(#D{Vj}zS_7v-1+JZ)Ut*3JAEfS%X{>0YBu-sP1gF+Q+Epqe)b@9_en8eF){FDs}D2UdYrn)&Asa z^-=i8YG1o-zeNlUo&LwV2)kaDmNY#*@B1fV@kBkddZNT*?p?EWf%MVW@o&7h(Nh7} z0fDlXUb|8?F?gZ~JE6)DRD3)#B!R;YUDSuSrKP?t#^VE4#XdoDME zHy4ZD4m#4d2}#7qnu_VRCH?#`SOtmhi;dZh0_{610Lh z+kM5}lcrqCegb0{NkB+N2@88)Q-cTT>qQ*_$Qy!5f2==F*GcBU*kDsmk{+w~ZsH!x z)87KIW|@a*W|UiSREewU^NCwk&AcvQbh_XH0~sp|<5)C;DIXOg<}T6?Z^7bt_r=j6 zdFx&gL}mV3ftJcnw@h<;!^_lOx|Gp7-sar3H|D{o`>s-z#yHq7uHO(%ZD1Lj&hJjb zBsM0LoH8~N!>=Qrey#+*FcxQ(hwZwoq81QWp1jA`oLBCP0WpxoIgGdd2IPs6qM_7K zhEpALQvFp&C6p+^d+@&p1^7p;wTQhGpBe0IaelJJcycFvxJ8o=_0BELOACgk@0qk# z4#(>AK30;MqqdZTXGU7>-2o=%uvL6TYCjwYGelWCi?@^{l#Pz7#Y$`6B00gA&o_ZX zKrZcPVmU1C0{OT_uQDWtsc-Mf6j?LWEhjmlS>;3+wtO(*Mj50jsSa zejET=$i0Wp<~kH%{+5O69bbqS%4PqSViwPZkPalZx#3$YO1viB+qd8ID#lS&4$$6VCBm-WCgAy$}R??5reN}ir8amzlZw* z1PiXIqZIH@A-VIPxuMA3chwHt0|AvkaJ`5p#ux_V-#^?%PN&c!niiLhQ=y1H=xgm?H_9XTdC zU~L>zLo>;M3~~;{k>9E81l91dE#^6OkO1kc8c!`xJ7IJ7<-k8%|8-*f^z+3?b9qi7 zMAGJb&bAX9?0en4FrNECVUn?xi>NnV?%Ix1Ki)7!iFf;XT>GHpb&w0*fSD9#M?HIs zC0VUU%$o@%N|^8F61uy?BMZS!F`}wdPWpLq>b02wIfb8+D8yx;ioYYx*`7(Y(Zmn7 zF$YdORXyfQh`KiW7yhuy)uRx_Oni7Lb}OxqjKZF%LHwf~pIIrgk#h_X>Npf%iuOg_ zBX9dDNuHXoNL5Ex%$L3|#j?i`L3SCWhHYyw0Yuuu6HCG^KQ@CU06>!X6)^WWwLVI< zBj_}H3&cot@;_4v9`iVKi&rg1$}wzBd6bd(GWnmkMPd7i3m$mxX z#Q)wv7K36`&bNpc)r-Yz1+_47UfX*SKAqe z|HH?}i@^Y-oCjgsdvRTKy8)aj6Ys}DVOp?sL!Wd^il(Ro4gpS#Bs6O^_{!n~;w)Wm z^&*nlx=7=GEe@C!TG^dHZv$a=f)nLe(~sWK$H$k94iO(t$;D6L|H0i9?up*EZgs+y z0!ma5{x(BJ-I%a6uvgSWEGc3Y#4N}%`HRf9DpDQ`ajT5fgj(g-vPcEOwR~buzgqF5 zEhsZ`@$B#ZK{Q5mmCq;$bL>}&j)=NpYb>`4Zm96v1ECzE`8;sHC@55_38fN-IFSZq z3knI)leRdlA!@>O#@s7|Ru;B}$bA`lZCzMWweOZXMQ$L`p`vDx4?fFXQRh5HRCx7{FKO#DTZfLbU{7)Fu z%%^PCQY><0Au@MBV8rc>n%si?0t&bD6hmKk&LpF9&=^HiCQ;bTd8k$Nh+3g*HdvtTzx9;(^QTRGU(| zNmESw0rlc}0bvF-U&OR8X)()6)i$)|=lO>^vZcypN$KLMUkE&Ks1@8Pyqdta3RrvZ zUYlQM!wmudnO|H2baO0%;6T~+1++AuoZ9`k(UBskdCuahFrb%JZsxK5S~AdRh__m5 z0GYBm7|xGoXa{+hkZnDWtreWxF+hwU%_v#GjIhuURE1kO)5If9<&cWHB*_jHV5(jtcm_i6s~-T zCG4(Df7l&i9yra?vJ-$I;2JByOLZ0@Lj})5Nu?0R{|O-u z-tpQgyTx^j3YN0-^02d^pezyb1IHTe*&YFG0%vo)VAgClK0gh#_M1%o6kI1~?kI1n zgK))gyis^ll<*W~wsR?)oX+VCssPdcddd({`T>JKq)U@Ebv1tYcMa))feI1*B$cxx zY=|vVnOB>j&d4`(>l0nYF=LDllI7M+PfZl-v~HVPYr##qU&mKfmtc?>*jIrLGGU1s zdjLa!B3L|zI9#bPwWvpm)Z!~AVidm=zHhH?Q3q{UU^pigV}yOv=w{oQsCuGVJ!;T9 z@L-G>A}Y z*ZXalv6=0?VHP>Ac7eotV}*huG|Upj@f)Re2h}4v2bd4w!0mUJSR*VOdC68@u$$?9 ztg}&8`c0Eap`wQ50xdUcv1BtupaGc^i8rK`v{Qpk6KeQk!Lb7i@o<;OGSXQnoEdo& zGc`!)s;@}Ku42;z&kUm0np^_nQN{%zJM~notkFV75b%aIY3?>LirC={#FP-+LRDB! zHo&hSxWXbM5>vcA{5{oVZfwtpJW&raAR+**ZN@xlJUTvfw-FY=Ocbwg3ECv`FMgY3 z`$cyG?s6sy76+Vph8oL*D)r4eJk@ZSOWu_}xNMV&5HuQ-g33u{w*}SGCsin|dR4nb zLMPGeFVWWEr3Pa>*>-$0o-SU}gM3x=jJ%puj*eYmk{C(>1R*L~=xj*wZZ631dK2m# zorz{sy(|v_v*=y~Wl(zWBjsfHk+K0# z%(3w6(?FW)(T!;qEV}88PSeyki>A(DmpUl|5OE98Qs@iB&9ILE6&L@u$z0G;Lj*y)*g)rh zpI^9;4j_SMfgZ=n`{c~i&!s&DUjb=y3e_15feUq~k`?K74^*V0L84Q`^l*V(whWq$ znj@NI`;>X-5{9R5sj6|f@>jjOb6bY4rL#ii1;!D*imtQSPTC_V9v5&SHXQo3$0_Ij3B=(I(F(lemD4C5oLqor< zMD(Lt+s`zu=-K-NJDj6i&2>Bwl=@=jon(jb?N)h|`3wNQ#MTvcBV$r8J)l__b7fSt z^hN3YZ)ICLfVoHOfL+EeYcl|8)Em+ek9~X9TV}J!pq&FQ zg5%6-3E=qJ!gU(sKB$I{SAj2zhWWz>OLXQ5@`~AeI~yer#X#2bYY3BGU#@=zM2)iu z;_`FDRG<#xU(KVXbq-&C>7!@s0p0n@!< z*wJ`e1^5oWlOkf||H7~9%EbkrKl;iuBLsZ*Mo6j=&?B^)TrTAd%rEF*#Rt#1L}52Mx3xc_0Bm|v+AM5n=OJdJ}9M_~FZO~H~%W@}U-gemSUQqIlAe6c@ ziMK(&Ropb>l1mbGn*dZr<+)GvP-oFGzMz!%!e0+iZ%GY-GJZ2*)&!Ll+pvijp%gUI zq)Y;LT*5IGH6qOzuu8Fbvb1`(`1iw#0AJ2u2pu&>NpWN+cYa(TdH`n;^FB|TQdFFR zi7^0RUyBq5RVD#j9xyA-rmm6+7*)OpKP|j+AX=duqBF^g77RZjqohWRmV?X+r0i;O zGZ-|<6xq>n{C6WTJxDLt5u#2=duJc2$#)vcyYx~Xk(OGNB+P?uVOGF<7csS04tW}o z!7f9)MOh}Ddon#Cz)ItRnM3F>sPm2leV`BSywZ-bFd!2PL}6}B9|AN38T0F?nkZg2 zyzw}KTvaFWbdpZjFQLqFHmy-y*dudB;Q1UcqST(o=Souq0*g^V#}+I77#l3iNRkaq zAOY)rrg+@pnkI5$c}qZoF)zue~9TD3i5T zC#B4rTa0Jnd^S+3-(OeKfCDcP1^kq=wjxGk3S%jy1ZzALoxY`PynGr(EUI#V(9n>! z78JHfIB!?_sfmFi-9mt((=#BEObAGL5D6~o)&6y|@&(D_H z0HBd;fW$Rs-c8XFl}efU5)6|TvnVdrR2AeU;E#}J@u zt3o(mtB&Lr_wK8Wq(2Hqwif7xx`q{2GXukjQ{W^8)%dOFBp9(&8qxK>|5|4BLg;-D*5V^bLaHha=EZkjz8oCx`BpT8riy5Fi6g2k`cqUu(-s==?WY)jd!r)&g5jC>H=-69rH^iFp&ev0`)UtRJ ztY&Qf7txD5n+2id0o({>6O4VPNzq3+n>U{lOfM%~a`O&dC(s z>WArpk|ru@D{7`Rrra{oAd0wJW~6Jq#gj6gK?rGp`eF@na#nofK*-jF2;uj-?tw2$ zK@);z)?}sn_{&Z8>)IVe!sOn9S(D&#%jRqnH3$fW86=Kl-MY?3U+Nlyy{By zOQxa+yBxB8p{?bi)T?Aag~SA0x#j7=9B-6?w3ok=D^Ui-20~!sxS2usVx}50sK{m^ ig3W + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-BoldItalic-webfont.woff b/docs/fonts/OpenSans-BoldItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ed760c0628b6a0026041f5b8bba466a0471fd2e0 GIT binary patch literal 23048 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Italic-webfont.woff b/docs/fonts/OpenSans-Italic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ff652e64356b538c001423b6aedefcf1ee66cd17 GIT binary patch literal 23188 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Light-webfont.svg b/docs/fonts/OpenSans-Light-webfont.svg new file mode 100644 index 0000000..11a472c --- /dev/null +++ b/docs/fonts/OpenSans-Light-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Light-webfont.woff b/docs/fonts/OpenSans-Light-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e786074813a27d0a7a249047832988d5bf0fe756 GIT binary patch literal 22248 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-LightItalic-webfont.eot b/docs/fonts/OpenSans-LightItalic-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..8f445929ffb03b50e98c2a2f7d831a0cb1b276a2 GIT binary patch literal 20535 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 Z%0#`NexefPFV?Xj51H#Y#AC7WXn+Jg($4?@ literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-LightItalic-webfont.svg b/docs/fonts/OpenSans-LightItalic-webfont.svg new file mode 100644 index 0000000..431d7e3 --- /dev/null +++ b/docs/fonts/OpenSans-LightItalic-webfont.svg @@ -0,0 +1,1835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-LightItalic-webfont.woff b/docs/fonts/OpenSans-LightItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..43e8b9e6cc061ff17fd2903075cbde12715512b3 GIT binary patch literal 23400 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 + + +
+
+ + + +
+
+
+ +
+
+
+

Title

+

Global

+
+ + + + + +
+ +
+ +

+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + +
+

Members

+
+ +
+ +

+ # + + + constant + + + + offliineSchema + + +

+ + + + +
+ schema for options object +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
See:
+
+ +
+ + + + + +

+ View Source + + offline/OfflinePlugin.js, line 23 + +

+ +
+ + + + + +
+ +
+ +

+ # + + + constant + + + + wascSchema + + +

+ + + + +
+ schema for options object +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
See:
+
+ +
+ + + + + +

+ View Source + + wasc-worker/WascBuilderPlugin.js, line 27 + +

+ +
+ + + + + +
+ +
+
+ + + +
+

Methods

+
+ +
+ + + +

+ # + + + + getAllFiles(baseDir, subDir, arrayOfFiles) → {array} + + +

+ + + + +
+ list files recursively +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
baseDir + + +strring + + + + start directory
subDir + + +string + + + + sub directory
arrayOfFiles + + +array + + + + result files
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + offline/OfflinePlugin.js, line 48 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
arrayOfFiles
+ + +
+ + +array + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + getTransferableParams(params) → {Object} + + +

+ + + + +
+ Filter out parameters for passing them into WebWorkers +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
params + + +Object + + + + The given Items to filter
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + wasc-worker/WascRT.ts, line 146 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
WebWorker-passable items
+ + +
+ + +Object + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + myFetch(path, resType, owMime) → {Object} + + +

+ + + + +
+ Small reusable fetch function, should work for local & web server files +
+ + + + + + + + + + +
Parameters:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
path + + +string + + + + file to request
resType + + +string + + + + type of data to request (default = arraybuffer)
owMime + + +string + + + + force-override mime-type (optional)
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + wasc-worker/WascRT.ts, line 116 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
XMLHttpRequest.response (converted to resType)
+ + +
+ + +Object + + +
+ +
+ + +
+
+ + + + +
+ +
+ + + +

+ # + + + + waitReady() → {Promise} + + +

+ + + + +
+ Helper function +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + Util.ts, line 17 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
resolve when site ready
+ + +
+ + +Promise + + +
+ +
+ + +
+
+ + + + +
+ +
+
+ + + + + +
+ +
+ + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..d7a3209 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,161 @@ + + + + + + + + Home + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

+

Home

+
+ + + + + + + +

+ + + + + + + + + + + + + + + +
+

we_utils

+

is a collection of utilities, mostly usefull when creating Wallpaper Engine Web Wallpapers with TypeScript / Webpack.

+

I created this repository since I was previously copying back-and-forth lots of code between projects. +Keeping track of this stuff manually is annoying...

+

Dependencies / Libraries

+ +

Features / Contents

+
    +
  • OfflineWorker
  • +
  • AssemblyScript Webpack Builder
  • +
  • AssemblyScript WebAssembly Module Loader
  • +
  • CComponent & CSettings Helpers
  • +
  • Wallpaper Engine Audio Supplier (WEAS)
  • +
  • "document.ready" shorthand
  • +
  • ReloadHelper for displaying a loading bar
  • +
  • Smallog Logging & Filtering
  • +
  • Stats.js definition file
  • +
  • WarnHelper for Seizure warnings
  • +
  • Wallpaper Engine iCUE Library (WEICUE)
  • +
  • Wallpaper Engine Wallpaper Adapter (WEWWA)
  • +
  • Worker-Loader definition file
  • +
+

Used by

+
+
+ + + + + + +
+ + + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/docs/offline_OfflinePlugin.js.html b/docs/offline_OfflinePlugin.js.html new file mode 100644 index 0000000..9ae3276 --- /dev/null +++ b/docs/offline_OfflinePlugin.js.html @@ -0,0 +1,266 @@ + + + + + + + + + + offline/OfflinePlugin.js + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ +
+
+
+

Source

+

offline/OfflinePlugin.js

+
+ + + + + +
+
+
/**
+* @author hexxone / https://hexx.one
+*
+* @license
+* Copyright (c) 2021 hexxone All rights reserved.
+* Licensed under the GNU GENERAL PUBLIC LICENSE.
+* See LICENSE file in the project root for full license information.
+* @ignore
+*/
+
+
+const fs = require('fs');
+const validate = require('schema-utils');
+const {Compilation} = require('webpack');
+const {RawSource} = require('webpack-sources');
+
+const pluginName = 'OfflinePlugin';
+
+/**
+ * schema for options object
+ * @see {OfflinePlugin}
+ */
+const offliineSchema = {
+	type: 'object',
+	properties: {
+		staticdir: {
+			type: 'string',
+		},
+		outfile: {
+			type: 'string',
+		},
+		extrafiles: {
+			type: 'array',
+		},
+		pretty: {
+			type: 'boolean',
+		},
+	},
+};
+
+/**
+* list files recursively
+* @param {strring} baseDir start directory
+* @param {string} subDir sub directory
+* @param {array} arrayOfFiles result files
+* @return {array} arrayOfFiles
+*/
+function getAllFiles(baseDir, subDir, arrayOfFiles) {
+	const sub = baseDir + '/' + subDir;
+	const files = fs.readdirSync(sub);
+	arrayOfFiles = arrayOfFiles || [];
+	files.forEach((file) => {
+		const fle = sub + '/' + file;
+		if (fs.statSync(fle).isDirectory()) {
+			arrayOfFiles = getAllFiles(baseDir, subDir + '/' + file, arrayOfFiles);
+		} else {
+			arrayOfFiles.push(subDir + '/' + file);
+		}
+	});
+	return arrayOfFiles;
+}
+
+/**
+* This is a webpack plugin for easily making your webapp available Offline.
+* <br/>
+* It will simply output a json list of files to cache for the Service Worker on build.
+* <br/>
+* In your source, you just 'require' and 'Register()' the OfflineHelper.
+* <br/>
+* The OfflineHelper will then require the OfflineWorker in background.
+* <br/>
+* <br/>
+*
+* The OfflineWorker will then:
+* <br/>
+* 1. launch itself
+* <br/>
+* 2. load the 'offlinefiles.json' list
+* <br/>
+* 3. cache all files in it
+* <br/>
+* 4. return cached files if network fetching fails
+* <br/>
+* <br/>
+* You can also ignore this plugin and create the 'offlinefiles.json' yourself.
+*/
+class OfflinePlugin {
+	options = {};
+
+	/**
+	* Intializes the plugin in the webpack build process
+	* @param {offliineSchema} options
+	*/
+	constructor(options = {}) {
+		validate.validate(offliineSchema, options);
+		this.options = options;
+	}
+
+	/**
+	* Hook into the compilation process,
+	* find all target files and put them into a json file
+	* @param {Webpack.compiler} compiler object from webpack
+	*/
+	apply(compiler) {
+		let addedOnce = false;
+		// Specify the event hook to attach to
+		compiler.hooks.thisCompilation.tap(pluginName, (compilation) =>
+			compilation.hooks.processAssets.tap({
+				name: pluginName,
+				stage: Compilation.PROCESS_ASSETS_STAGE_ANALYSE,
+			}, () => {
+				if (addedOnce) return;
+				addedOnce = true;
+
+				console.info('[' + pluginName + '] Gathering Infos...');
+
+				// list of all app-contents
+				const filelist = [];
+
+				// add static files from folder
+				const sFiles = getAllFiles(this.options.staticdir, '');
+				for (const staticFile in sFiles) {
+					if (!staticFile) continue;
+					filelist.push(sFiles[staticFile]);
+				}
+
+				// Loop through all compiled assets,
+				// adding a new line item for each filename.
+				for (const filename in compilation.assets) {
+					if (!filename) continue;
+					filelist.push('/' + filename);
+				}
+
+				// add additional files anyway?
+				if (this.options.extrafiles) {
+					this.options.extrafiles.map((ef) => filelist.push(ef));
+				}
+
+				// create the target file with all app-contents as json list
+				const jList = JSON.stringify(filelist, null, this.options.pretty ? 1 : 0);
+				compilation.emitAsset(this.options.outfile, new RawSource(jList));
+				console.info('[' + pluginName + '] result: ' + jList);
+
+				console.info('[' + pluginName + '] finished.');
+			},
+			));
+	}
+}
+
+module.exports = OfflinePlugin;
+
+
+
+ + + + +
+ + + +
+
+
+
+ + + + + + diff --git a/docs/scripts/app.min.js b/docs/scripts/app.min.js new file mode 100644 index 0000000..9411fc0 --- /dev/null +++ b/docs/scripts/app.min.js @@ -0,0 +1 @@ +"use strict";$().ready(function(){});var sidebarIsVisible=!1,toggleSidebar=function(e){var a=!(0 h1").text();if(t){o.append($("

").text(t));var s=$("
    ");i.find(".members h4.name").each(function(e,a){var i=$(a),t=i.find(".code-name").clone().children().remove().end().text(),n=i.find("a").attr("href"),r=$('')).text(t);s.append($("
  • ").append(r)),c.push({link:r,offset:i.offset().top})}),o.append(s)}else i.find(".members h4.name").each(function(e,a){var i=$(a),t=i.find(".code-name").clone().children().remove().end().text(),n=i.find("a").attr("href"),r=$('
    ')).text(t);o.append(r),c.push({link:r,offset:i.offset().top})})}),!$.trim(o.text()))return o.hide();function e(){for(var e=n.scrollTop(),a=!1,i=c.length-1;0<=i;i--){var t=c[i];t.link.removeClass("is-active"),e+OFFSET>=t.offset?a?t.link.addClass("is-past"):(t.link.addClass("is-active"),a=!0):t.link.removeClass("is-past")}}var n=$("#main-content-wrapper");n.on("scroll",e),e(),c.forEach(function(e){e.link.click(function(){n.animate({scrollTop:e.offset-OFFSET+1},500)})})}),$().ready(function(){$("#sidebarNav a").each(function(e,a){var i=$(a).attr("href");window.location.pathname.match("/"+i)&&($(a).addClass("active"),$("#sidebarNav").scrollTop($(a).offset().top-150))})}); \ No newline at end of file diff --git a/docs/scripts/linenumber.js b/docs/scripts/linenumber.js new file mode 100644 index 0000000..1ba4057 --- /dev/null +++ b/docs/scripts/linenumber.js @@ -0,0 +1,26 @@ +/*global document */ + +(function() { + var source = document.getElementsByClassName('prettyprint source linenums'); + var i = 0; + var lineNumber = 0; + var lineId; + var lines; + var totalLines; + var anchorHash; + + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName('li'); + totalLines = lines.length; + + for (; i < totalLines; i++) { + lineNumber++; + lineId = 'line' + lineNumber; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += ' selected'; + } + } + } +})(); diff --git a/docs/scripts/prettify/Apache-License-2.0.txt b/docs/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/docs/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/docs/scripts/prettify/lang-css.js b/docs/scripts/prettify/lang-css.js new file mode 100644 index 0000000..041e1f5 --- /dev/null +++ b/docs/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/docs/scripts/prettify/prettify.js b/docs/scripts/prettify/prettify.js new file mode 100644 index 0000000..eef5ad7 --- /dev/null +++ b/docs/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p { + // loop over each targets and hide the not corresponding ones + targets.forEach(target => { + if (!target.innerText.toLowerCase().includes(input.value.toLowerCase())) { + target.style.display = 'none' + + /** + * Detects an empty list + * Remove the list and the list's title if the list is not displayed + */ + const list = [...target.parentNode.childNodes].filter( elem => elem.style.display !== 'none') + + if (!list.length) { + target.parentNode.style.display = 'none' + target.parentNode.previousSibling.style.display = 'none' + } + + /** + * Detects empty category + * Remove the entire category if no item is displayed + */ + const category = [...target.parentNode.parentNode.childNodes] + .filter( elem => elem.tagName !== 'H2' && elem.style.display !== 'none') + + if (!category.length) { + target.parentNode.parentNode.style.display = 'none' + } + } else { + target.parentNode.style.display = 'block' + target.parentNode.previousSibling.style.display = 'block' + target.parentNode.parentNode.style.display = 'block' + target.style.display = 'block' + } + }) + }) +})() \ No newline at end of file diff --git a/docs/styles/app.min.css b/docs/styles/app.min.css new file mode 100644 index 0000000..1620752 --- /dev/null +++ b/docs/styles/app.min.css @@ -0,0 +1 @@ +/*! bulma.io v0.7.5 | MIT License | github.com/jgthms/bulma */@-webkit-keyframes spinAround{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.delete,.modal-close,.is-unselectable,.button,.file,.breadcrumb,.pagination-previous,.pagination-next,.pagination-link,.pagination-ellipsis,.tabs{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.select:not(.is-multiple):not(.is-loading)::after,.navbar-link:not(.is-arrowless)::after{border:3px solid rgba(0,0,0,0);border-radius:2px;border-right:0;border-top:0;content:" ";display:block;height:0.625em;margin-top:-0.4375em;pointer-events:none;position:absolute;top:50%;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:center;transform-origin:center;width:0.625em}.box:not(:last-child),.content:not(:last-child),.notification:not(:last-child),.progress:not(:last-child),.table:not(:last-child),.table-container:not(:last-child),.title:not(:last-child),.subtitle:not(:last-child),.block:not(:last-child),.highlight:not(:last-child),.breadcrumb:not(:last-child),.level:not(:last-child),.list:not(:last-child),.message:not(:last-child),.tabs:not(:last-child){margin-bottom:1.5rem}.delete,.modal-close{-moz-appearance:none;-webkit-appearance:none;background-color:rgba(10,10,10,0.2);border:none;border-radius:290486px;cursor:pointer;pointer-events:auto;display:inline-block;flex-grow:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:none;position:relative;vertical-align:top;width:20px}.delete::before,.modal-close::before,.delete::after,.modal-close::after{background-color:#fff;content:"";display:block;left:50%;position:absolute;top:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);-webkit-transform-origin:center center;transform-origin:center center}.delete::before,.modal-close::before{height:2px;width:50%}.delete::after,.modal-close::after{height:50%;width:2px}.delete:hover,.modal-close:hover,.delete:focus,.modal-close:focus{background-color:rgba(10,10,10,0.3)}.delete:active,.modal-close:active{background-color:rgba(10,10,10,0.4)}.is-small.delete,.is-small.modal-close{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.is-medium.delete,.is-medium.modal-close{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.is-large.delete,.is-large.modal-close{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.button.is-loading::after,.loader,.select.is-loading::after,.control.is-loading::after{-webkit-animation:spinAround 500ms infinite linear;animation:spinAround 500ms infinite linear;border:2px solid #dbdbdb;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em}.is-overlay,.image.is-square img,.image.is-square .has-ratio,.image.is-1by1 img,.image.is-1by1 .has-ratio,.image.is-5by4 img,.image.is-5by4 .has-ratio,.image.is-4by3 img,.image.is-4by3 .has-ratio,.image.is-3by2 img,.image.is-3by2 .has-ratio,.image.is-5by3 img,.image.is-5by3 .has-ratio,.image.is-16by9 img,.image.is-16by9 .has-ratio,.image.is-2by1 img,.image.is-2by1 .has-ratio,.image.is-3by1 img,.image.is-3by1 .has-ratio,.image.is-4by5 img,.image.is-4by5 .has-ratio,.image.is-3by4 img,.image.is-3by4 .has-ratio,.image.is-2by3 img,.image.is-2by3 .has-ratio,.image.is-3by5 img,.image.is-3by5 .has-ratio,.image.is-9by16 img,.image.is-9by16 .has-ratio,.image.is-1by2 img,.image.is-1by2 .has-ratio,.image.is-1by3 img,.image.is-1by3 .has-ratio,.modal,.modal-background,.hero-video{bottom:0;left:0;position:absolute;right:0;top:0}.button,.input,.textarea,.select select,.file-cta,.file-name,.pagination-previous,.pagination-next,.pagination-link,.pagination-ellipsis{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:4px;box-shadow:none;display:inline-flex;font-size:1rem;height:2.25em;justify-content:flex-start;line-height:1.5;padding-bottom:calc(0.375em - 1px);padding-left:calc(0.625em - 1px);padding-right:calc(0.625em - 1px);padding-top:calc(0.375em - 1px);position:relative;vertical-align:top}.button:focus,.input:focus,.textarea:focus,.select select:focus,.file-cta:focus,.file-name:focus,.pagination-previous:focus,.pagination-next:focus,.pagination-link:focus,.pagination-ellipsis:focus,.is-focused.button,.is-focused.input,.is-focused.textarea,.select select.is-focused,.is-focused.file-cta,.is-focused.file-name,.is-focused.pagination-previous,.is-focused.pagination-next,.is-focused.pagination-link,.is-focused.pagination-ellipsis,.button:active,.input:active,.textarea:active,.select select:active,.file-cta:active,.file-name:active,.pagination-previous:active,.pagination-next:active,.pagination-link:active,.pagination-ellipsis:active,.is-active.button,.is-active.input,.is-active.textarea,.select select.is-active,.is-active.file-cta,.is-active.file-name,.is-active.pagination-previous,.is-active.pagination-next,.is-active.pagination-link,.is-active.pagination-ellipsis{outline:none}.button[disabled],.input[disabled],.textarea[disabled],.select select[disabled],.file-cta[disabled],.file-name[disabled],.pagination-previous[disabled],.pagination-next[disabled],.pagination-link[disabled],.pagination-ellipsis[disabled],fieldset[disabled] .button,fieldset[disabled] .input,fieldset[disabled] .textarea,fieldset[disabled] .select select,.select fieldset[disabled] select,fieldset[disabled] .file-cta,fieldset[disabled] .file-name,fieldset[disabled] .pagination-previous,fieldset[disabled] .pagination-next,fieldset[disabled] .pagination-link,fieldset[disabled] .pagination-ellipsis{cursor:not-allowed}/*! minireset.css v0.0.4 | MIT License | github.com/jgthms/minireset.css */html,body,p,ol,ul,li,dl,dt,dd,blockquote,figure,fieldset,legend,textarea,pre,iframe,hr,h1,h2,h3,h4,h5,h6{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,*::before,*::after{box-sizing:inherit}img,embed,iframe,object,video{height:auto;max-width:100%}audio{max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}td:not([align]),th:not([align]){text-align:left}html{background-color:#fff;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:hidden;overflow-y:scroll;text-rendering:optimizeLegibility;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}article,aside,figure,footer,header,hgroup,section{display:block}body,button,input,select,textarea{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue","Helvetica","Arial",sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:monospace}body{color:#4a4a4a;font-size:1em;font-weight:400;line-height:1.5}a{color:#3273dc;cursor:pointer;text-decoration:none}a strong{color:currentColor}a:hover{color:#363636}code{background-color:#f5f5f5;color:#ff3860;font-size:.875em;font-weight:normal;padding:0.25em 0.5em 0.25em}hr{background-color:#f5f5f5;border:none;display:block;height:2px;margin:1.5rem 0}img{height:auto;max-width:100%}input[type="checkbox"],input[type="radio"]{vertical-align:baseline}small{font-size:.875em}span{font-style:inherit;font-weight:inherit}strong{color:#363636;font-weight:700}fieldset{border:none}pre{-webkit-overflow-scrolling:touch;background-color:#f5f5f5;color:#4a4a4a;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}table td,table th{vertical-align:top}table td:not([align]),table th:not([align]){text-align:left}table th{color:#363636}.is-clearfix::after{clear:both;content:" ";display:table}.is-pulled-left{float:left !important}.is-pulled-right{float:right !important}.is-clipped{overflow:hidden !important}.is-size-1{font-size:3rem !important}.is-size-2{font-size:2.5rem !important}.is-size-3{font-size:2rem !important}.is-size-4{font-size:1.5rem !important}.is-size-5{font-size:1.25rem !important}.is-size-6{font-size:1rem !important}.is-size-7{font-size:.75rem !important}@media screen and (max-width: 768px){.is-size-1-mobile{font-size:3rem !important}.is-size-2-mobile{font-size:2.5rem !important}.is-size-3-mobile{font-size:2rem !important}.is-size-4-mobile{font-size:1.5rem !important}.is-size-5-mobile{font-size:1.25rem !important}.is-size-6-mobile{font-size:1rem !important}.is-size-7-mobile{font-size:.75rem !important}}@media screen and (min-width: 769px), print{.is-size-1-tablet{font-size:3rem !important}.is-size-2-tablet{font-size:2.5rem !important}.is-size-3-tablet{font-size:2rem !important}.is-size-4-tablet{font-size:1.5rem !important}.is-size-5-tablet{font-size:1.25rem !important}.is-size-6-tablet{font-size:1rem !important}.is-size-7-tablet{font-size:.75rem !important}}@media screen and (max-width: 1023px){.is-size-1-touch{font-size:3rem !important}.is-size-2-touch{font-size:2.5rem !important}.is-size-3-touch{font-size:2rem !important}.is-size-4-touch{font-size:1.5rem !important}.is-size-5-touch{font-size:1.25rem !important}.is-size-6-touch{font-size:1rem !important}.is-size-7-touch{font-size:.75rem !important}}@media screen and (min-width: 1024px){.is-size-1-desktop{font-size:3rem !important}.is-size-2-desktop{font-size:2.5rem !important}.is-size-3-desktop{font-size:2rem !important}.is-size-4-desktop{font-size:1.5rem !important}.is-size-5-desktop{font-size:1.25rem !important}.is-size-6-desktop{font-size:1rem !important}.is-size-7-desktop{font-size:.75rem !important}}@media screen and (min-width: 1216px){.is-size-1-widescreen{font-size:3rem !important}.is-size-2-widescreen{font-size:2.5rem !important}.is-size-3-widescreen{font-size:2rem !important}.is-size-4-widescreen{font-size:1.5rem !important}.is-size-5-widescreen{font-size:1.25rem !important}.is-size-6-widescreen{font-size:1rem !important}.is-size-7-widescreen{font-size:.75rem !important}}@media screen and (min-width: 1408px){.is-size-1-fullhd{font-size:3rem !important}.is-size-2-fullhd{font-size:2.5rem !important}.is-size-3-fullhd{font-size:2rem !important}.is-size-4-fullhd{font-size:1.5rem !important}.is-size-5-fullhd{font-size:1.25rem !important}.is-size-6-fullhd{font-size:1rem !important}.is-size-7-fullhd{font-size:.75rem !important}}.has-text-centered{text-align:center !important}.has-text-justified{text-align:justify !important}.has-text-left{text-align:left !important}.has-text-right{text-align:right !important}@media screen and (max-width: 768px){.has-text-centered-mobile{text-align:center !important}}@media screen and (min-width: 769px), print{.has-text-centered-tablet{text-align:center !important}}@media screen and (min-width: 769px) and (max-width: 1023px){.has-text-centered-tablet-only{text-align:center !important}}@media screen and (max-width: 1023px){.has-text-centered-touch{text-align:center !important}}@media screen and (min-width: 1024px){.has-text-centered-desktop{text-align:center !important}}@media screen and (min-width: 1024px) and (max-width: 1215px){.has-text-centered-desktop-only{text-align:center !important}}@media screen and (min-width: 1216px){.has-text-centered-widescreen{text-align:center !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-centered-widescreen-only{text-align:center !important}}@media screen and (min-width: 1408px){.has-text-centered-fullhd{text-align:center !important}}@media screen and (max-width: 768px){.has-text-justified-mobile{text-align:justify !important}}@media screen and (min-width: 769px), print{.has-text-justified-tablet{text-align:justify !important}}@media screen and (min-width: 769px) and (max-width: 1023px){.has-text-justified-tablet-only{text-align:justify !important}}@media screen and (max-width: 1023px){.has-text-justified-touch{text-align:justify !important}}@media screen and (min-width: 1024px){.has-text-justified-desktop{text-align:justify !important}}@media screen and (min-width: 1024px) and (max-width: 1215px){.has-text-justified-desktop-only{text-align:justify !important}}@media screen and (min-width: 1216px){.has-text-justified-widescreen{text-align:justify !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-justified-widescreen-only{text-align:justify !important}}@media screen and (min-width: 1408px){.has-text-justified-fullhd{text-align:justify !important}}@media screen and (max-width: 768px){.has-text-left-mobile{text-align:left !important}}@media screen and (min-width: 769px), print{.has-text-left-tablet{text-align:left !important}}@media screen and (min-width: 769px) and (max-width: 1023px){.has-text-left-tablet-only{text-align:left !important}}@media screen and (max-width: 1023px){.has-text-left-touch{text-align:left !important}}@media screen and (min-width: 1024px){.has-text-left-desktop{text-align:left !important}}@media screen and (min-width: 1024px) and (max-width: 1215px){.has-text-left-desktop-only{text-align:left !important}}@media screen and (min-width: 1216px){.has-text-left-widescreen{text-align:left !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-left-widescreen-only{text-align:left !important}}@media screen and (min-width: 1408px){.has-text-left-fullhd{text-align:left !important}}@media screen and (max-width: 768px){.has-text-right-mobile{text-align:right !important}}@media screen and (min-width: 769px), print{.has-text-right-tablet{text-align:right !important}}@media screen and (min-width: 769px) and (max-width: 1023px){.has-text-right-tablet-only{text-align:right !important}}@media screen and (max-width: 1023px){.has-text-right-touch{text-align:right !important}}@media screen and (min-width: 1024px){.has-text-right-desktop{text-align:right !important}}@media screen and (min-width: 1024px) and (max-width: 1215px){.has-text-right-desktop-only{text-align:right !important}}@media screen and (min-width: 1216px){.has-text-right-widescreen{text-align:right !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-right-widescreen-only{text-align:right !important}}@media screen and (min-width: 1408px){.has-text-right-fullhd{text-align:right !important}}.is-capitalized{text-transform:capitalize !important}.is-lowercase{text-transform:lowercase !important}.is-uppercase{text-transform:uppercase !important}.is-italic{font-style:italic !important}.has-text-white{color:#fff !important}a.has-text-white:hover,a.has-text-white:focus{color:#e6e6e6 !important}.has-background-white{background-color:#fff !important}.has-text-black{color:#0a0a0a !important}a.has-text-black:hover,a.has-text-black:focus{color:#000 !important}.has-background-black{background-color:#0a0a0a !important}.has-text-light{color:#f5f5f5 !important}a.has-text-light:hover,a.has-text-light:focus{color:#dbdbdb !important}.has-background-light{background-color:#f5f5f5 !important}.has-text-dark{color:#363636 !important}a.has-text-dark:hover,a.has-text-dark:focus{color:#1c1c1c !important}.has-background-dark{background-color:#363636 !important}.has-text-primary{color:#00d1b2 !important}a.has-text-primary:hover,a.has-text-primary:focus{color:#009e86 !important}.has-background-primary{background-color:#00d1b2 !important}.has-text-link{color:#3273dc !important}a.has-text-link:hover,a.has-text-link:focus{color:#205bbc !important}.has-background-link{background-color:#3273dc !important}.has-text-info{color:#209cee !important}a.has-text-info:hover,a.has-text-info:focus{color:#0f81cc !important}.has-background-info{background-color:#209cee !important}.has-text-success{color:#23d160 !important}a.has-text-success:hover,a.has-text-success:focus{color:#1ca64c !important}.has-background-success{background-color:#23d160 !important}.has-text-warning{color:#ffdd57 !important}a.has-text-warning:hover,a.has-text-warning:focus{color:#ffd324 !important}.has-background-warning{background-color:#ffdd57 !important}.has-text-danger{color:#ff3860 !important}a.has-text-danger:hover,a.has-text-danger:focus{color:#ff0537 !important}.has-background-danger{background-color:#ff3860 !important}.has-text-black-bis{color:#121212 !important}.has-background-black-bis{background-color:#121212 !important}.has-text-black-ter{color:#242424 !important}.has-background-black-ter{background-color:#242424 !important}.has-text-grey-darker{color:#363636 !important}.has-background-grey-darker{background-color:#363636 !important}.has-text-grey-dark{color:#4a4a4a !important}.has-background-grey-dark{background-color:#4a4a4a !important}.has-text-grey{color:#7a7a7a !important}.has-background-grey{background-color:#7a7a7a !important}.has-text-grey-light{color:#b5b5b5 !important}.has-background-grey-light{background-color:#b5b5b5 !important}.has-text-grey-lighter{color:#dbdbdb !important}.has-background-grey-lighter{background-color:#dbdbdb !important}.has-text-white-ter{color:#f5f5f5 !important}.has-background-white-ter{background-color:#f5f5f5 !important}.has-text-white-bis{color:#fafafa !important}.has-background-white-bis{background-color:#fafafa !important}.has-text-weight-light{font-weight:300 !important}.has-text-weight-normal{font-weight:400 !important}.has-text-weight-medium{font-weight:500 !important}.has-text-weight-semibold{font-weight:600 !important}.has-text-weight-bold{font-weight:700 !important}.is-family-primary{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue","Helvetica","Arial",sans-serif !important}.is-family-secondary{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue","Helvetica","Arial",sans-serif !important}.is-family-sans-serif{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue","Helvetica","Arial",sans-serif !important}.is-family-monospace{font-family:monospace !important}.is-family-code{font-family:monospace !important}.is-block{display:block !important}@media screen and (max-width: 768px){.is-block-mobile{display:block !important}}@media screen and (min-width: 769px), print{.is-block-tablet{display:block !important}}@media screen and (min-width: 769px) and (max-width: 1023px){.is-block-tablet-only{display:block !important}}@media screen and (max-width: 1023px){.is-block-touch{display:block !important}}@media screen and (min-width: 1024px){.is-block-desktop{display:block !important}}@media screen and (min-width: 1024px) and (max-width: 1215px){.is-block-desktop-only{display:block !important}}@media screen and (min-width: 1216px){.is-block-widescreen{display:block !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-block-widescreen-only{display:block !important}}@media screen and (min-width: 1408px){.is-block-fullhd{display:block !important}}.is-flex{display:flex !important}@media screen and (max-width: 768px){.is-flex-mobile{display:flex !important}}@media screen and (min-width: 769px), print{.is-flex-tablet{display:flex !important}}@media screen and (min-width: 769px) and (max-width: 1023px){.is-flex-tablet-only{display:flex !important}}@media screen and (max-width: 1023px){.is-flex-touch{display:flex !important}}@media screen and (min-width: 1024px){.is-flex-desktop{display:flex !important}}@media screen and (min-width: 1024px) and (max-width: 1215px){.is-flex-desktop-only{display:flex !important}}@media screen and (min-width: 1216px){.is-flex-widescreen{display:flex !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-flex-widescreen-only{display:flex !important}}@media screen and (min-width: 1408px){.is-flex-fullhd{display:flex !important}}.is-inline{display:inline !important}@media screen and (max-width: 768px){.is-inline-mobile{display:inline !important}}@media screen and (min-width: 769px), print{.is-inline-tablet{display:inline !important}}@media screen and (min-width: 769px) and (max-width: 1023px){.is-inline-tablet-only{display:inline !important}}@media screen and (max-width: 1023px){.is-inline-touch{display:inline !important}}@media screen and (min-width: 1024px){.is-inline-desktop{display:inline !important}}@media screen and (min-width: 1024px) and (max-width: 1215px){.is-inline-desktop-only{display:inline !important}}@media screen and (min-width: 1216px){.is-inline-widescreen{display:inline !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-inline-widescreen-only{display:inline !important}}@media screen and (min-width: 1408px){.is-inline-fullhd{display:inline !important}}.is-inline-block{display:inline-block !important}@media screen and (max-width: 768px){.is-inline-block-mobile{display:inline-block !important}}@media screen and (min-width: 769px), print{.is-inline-block-tablet{display:inline-block !important}}@media screen and (min-width: 769px) and (max-width: 1023px){.is-inline-block-tablet-only{display:inline-block !important}}@media screen and (max-width: 1023px){.is-inline-block-touch{display:inline-block !important}}@media screen and (min-width: 1024px){.is-inline-block-desktop{display:inline-block !important}}@media screen and (min-width: 1024px) and (max-width: 1215px){.is-inline-block-desktop-only{display:inline-block !important}}@media screen and (min-width: 1216px){.is-inline-block-widescreen{display:inline-block !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-inline-block-widescreen-only{display:inline-block !important}}@media screen and (min-width: 1408px){.is-inline-block-fullhd{display:inline-block !important}}.is-inline-flex{display:inline-flex !important}@media screen and (max-width: 768px){.is-inline-flex-mobile{display:inline-flex !important}}@media screen and (min-width: 769px), print{.is-inline-flex-tablet{display:inline-flex !important}}@media screen and (min-width: 769px) and (max-width: 1023px){.is-inline-flex-tablet-only{display:inline-flex !important}}@media screen and (max-width: 1023px){.is-inline-flex-touch{display:inline-flex !important}}@media screen and (min-width: 1024px){.is-inline-flex-desktop{display:inline-flex !important}}@media screen and (min-width: 1024px) and (max-width: 1215px){.is-inline-flex-desktop-only{display:inline-flex !important}}@media screen and (min-width: 1216px){.is-inline-flex-widescreen{display:inline-flex !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-inline-flex-widescreen-only{display:inline-flex !important}}@media screen and (min-width: 1408px){.is-inline-flex-fullhd{display:inline-flex !important}}.is-hidden{display:none !important}.is-sr-only{border:none !important;clip:rect(0, 0, 0, 0) !important;height:0.01em !important;overflow:hidden !important;padding:0 !important;position:absolute !important;white-space:nowrap !important;width:0.01em !important}@media screen and (max-width: 768px){.is-hidden-mobile{display:none !important}}@media screen and (min-width: 769px), print{.is-hidden-tablet{display:none !important}}@media screen and (min-width: 769px) and (max-width: 1023px){.is-hidden-tablet-only{display:none !important}}@media screen and (max-width: 1023px){.is-hidden-touch{display:none !important}}@media screen and (min-width: 1024px){.is-hidden-desktop{display:none !important}}@media screen and (min-width: 1024px) and (max-width: 1215px){.is-hidden-desktop-only{display:none !important}}@media screen and (min-width: 1216px){.is-hidden-widescreen{display:none !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-hidden-widescreen-only{display:none !important}}@media screen and (min-width: 1408px){.is-hidden-fullhd{display:none !important}}.is-invisible{visibility:hidden !important}@media screen and (max-width: 768px){.is-invisible-mobile{visibility:hidden !important}}@media screen and (min-width: 769px), print{.is-invisible-tablet{visibility:hidden !important}}@media screen and (min-width: 769px) and (max-width: 1023px){.is-invisible-tablet-only{visibility:hidden !important}}@media screen and (max-width: 1023px){.is-invisible-touch{visibility:hidden !important}}@media screen and (min-width: 1024px){.is-invisible-desktop{visibility:hidden !important}}@media screen and (min-width: 1024px) and (max-width: 1215px){.is-invisible-desktop-only{visibility:hidden !important}}@media screen and (min-width: 1216px){.is-invisible-widescreen{visibility:hidden !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-invisible-widescreen-only{visibility:hidden !important}}@media screen and (min-width: 1408px){.is-invisible-fullhd{visibility:hidden !important}}.is-marginless{margin:0 !important}.is-paddingless{padding:0 !important}.is-radiusless{border-radius:0 !important}.is-shadowless{box-shadow:none !important}.is-relative{position:relative !important}.box{background-color:#fff;border-radius:6px;box-shadow:0 2px 3px rgba(10,10,10,0.1),0 0 0 1px rgba(10,10,10,0.1);color:#4a4a4a;display:block;padding:1.25rem}a.box:hover,a.box:focus{box-shadow:0 2px 3px rgba(10,10,10,0.1),0 0 0 1px #3273dc}a.box:active{box-shadow:inset 0 1px 2px rgba(10,10,10,0.2),0 0 0 1px #3273dc}.button{background-color:#fff;border-color:#dbdbdb;border-width:1px;color:#363636;cursor:pointer;justify-content:center;padding-bottom:calc(0.375em - 1px);padding-left:.75em;padding-right:.75em;padding-top:calc(0.375em - 1px);text-align:center;white-space:nowrap}.button strong{color:inherit}.button .icon,.button .icon.is-small,.button .icon.is-medium,.button .icon.is-large{height:1.5em;width:1.5em}.button .icon:first-child:not(:last-child){margin-left:calc(-0.375em - 1px);margin-right:0.1875em}.button .icon:last-child:not(:first-child){margin-left:0.1875em;margin-right:calc(-0.375em - 1px)}.button .icon:first-child:last-child{margin-left:calc(-0.375em - 1px);margin-right:calc(-0.375em - 1px)}.button:hover,.button.is-hovered{border-color:#b5b5b5;color:#363636}.button:focus,.button.is-focused{border-color:#3273dc;color:#363636}.button:focus:not(:active),.button.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(50,115,220,0.25)}.button:active,.button.is-active{border-color:#4a4a4a;color:#363636}.button.is-text{background-color:transparent;border-color:transparent;color:#4a4a4a;text-decoration:underline}.button.is-text:hover,.button.is-text.is-hovered,.button.is-text:focus,.button.is-text.is-focused{background-color:#f5f5f5;color:#363636}.button.is-text:active,.button.is-text.is-active{background-color:#e8e8e8;color:#363636}.button.is-text[disabled],fieldset[disabled] .button.is-text{background-color:transparent;border-color:transparent;box-shadow:none}.button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}.button.is-white:hover,.button.is-white.is-hovered{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.button.is-white:focus,.button.is-white.is-focused{border-color:transparent;color:#0a0a0a}.button.is-white:focus:not(:active),.button.is-white.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(255,255,255,0.25)}.button.is-white:active,.button.is-white.is-active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.button.is-white[disabled],fieldset[disabled] .button.is-white{background-color:#fff;border-color:transparent;box-shadow:none}.button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted:hover,.button.is-white.is-inverted.is-hovered{background-color:#000}.button.is-white.is-inverted[disabled],fieldset[disabled] .button.is-white.is-inverted{background-color:#0a0a0a;border-color:transparent;box-shadow:none;color:#fff}.button.is-white.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}.button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-white.is-outlined:hover,.button.is-white.is-outlined.is-hovered,.button.is-white.is-outlined:focus,.button.is-white.is-outlined.is-focused{background-color:#fff;border-color:#fff;color:#0a0a0a}.button.is-white.is-outlined.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-white.is-outlined.is-loading:hover::after,.button.is-white.is-outlined.is-loading.is-hovered::after,.button.is-white.is-outlined.is-loading:focus::after,.button.is-white.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}.button.is-white.is-outlined[disabled],fieldset[disabled] .button.is-white.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-white.is-inverted.is-outlined:hover,.button.is-white.is-inverted.is-outlined.is-hovered,.button.is-white.is-inverted.is-outlined:focus,.button.is-white.is-inverted.is-outlined.is-focused{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-outlined.is-loading:hover::after,.button.is-white.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-white.is-inverted.is-outlined.is-loading:focus::after,.button.is-white.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-white.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}.button.is-black:hover,.button.is-black.is-hovered{background-color:#040404;border-color:transparent;color:#fff}.button.is-black:focus,.button.is-black.is-focused{border-color:transparent;color:#fff}.button.is-black:focus:not(:active),.button.is-black.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(10,10,10,0.25)}.button.is-black:active,.button.is-black.is-active{background-color:#000;border-color:transparent;color:#fff}.button.is-black[disabled],fieldset[disabled] .button.is-black{background-color:#0a0a0a;border-color:transparent;box-shadow:none}.button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted:hover,.button.is-black.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-black.is-inverted[disabled],fieldset[disabled] .button.is-black.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#0a0a0a}.button.is-black.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-black.is-outlined:hover,.button.is-black.is-outlined.is-hovered,.button.is-black.is-outlined:focus,.button.is-black.is-outlined.is-focused{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.button.is-black.is-outlined.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}.button.is-black.is-outlined.is-loading:hover::after,.button.is-black.is-outlined.is-loading.is-hovered::after,.button.is-black.is-outlined.is-loading:focus::after,.button.is-black.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-black.is-outlined[disabled],fieldset[disabled] .button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-black.is-inverted.is-outlined:hover,.button.is-black.is-inverted.is-outlined.is-hovered,.button.is-black.is-inverted.is-outlined:focus,.button.is-black.is-inverted.is-outlined.is-focused{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-outlined.is-loading:hover::after,.button.is-black.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-black.is-inverted.is-outlined.is-loading:focus::after,.button.is-black.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}.button.is-black.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-light{background-color:#f5f5f5;border-color:transparent;color:#363636}.button.is-light:hover,.button.is-light.is-hovered{background-color:#eee;border-color:transparent;color:#363636}.button.is-light:focus,.button.is-light.is-focused{border-color:transparent;color:#363636}.button.is-light:focus:not(:active),.button.is-light.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(245,245,245,0.25)}.button.is-light:active,.button.is-light.is-active{background-color:#e8e8e8;border-color:transparent;color:#363636}.button.is-light[disabled],fieldset[disabled] .button.is-light{background-color:#f5f5f5;border-color:transparent;box-shadow:none}.button.is-light.is-inverted{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted:hover,.button.is-light.is-inverted.is-hovered{background-color:#292929}.button.is-light.is-inverted[disabled],fieldset[disabled] .button.is-light.is-inverted{background-color:#363636;border-color:transparent;box-shadow:none;color:#f5f5f5}.button.is-light.is-loading::after{border-color:transparent transparent #363636 #363636 !important}.button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-light.is-outlined:hover,.button.is-light.is-outlined.is-hovered,.button.is-light.is-outlined:focus,.button.is-light.is-outlined.is-focused{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.button.is-light.is-outlined.is-loading::after{border-color:transparent transparent #f5f5f5 #f5f5f5 !important}.button.is-light.is-outlined.is-loading:hover::after,.button.is-light.is-outlined.is-loading.is-hovered::after,.button.is-light.is-outlined.is-loading:focus::after,.button.is-light.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #363636 #363636 !important}.button.is-light.is-outlined[disabled],fieldset[disabled] .button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-light.is-inverted.is-outlined:hover,.button.is-light.is-inverted.is-outlined.is-hovered,.button.is-light.is-inverted.is-outlined:focus,.button.is-light.is-inverted.is-outlined.is-focused{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted.is-outlined.is-loading:hover::after,.button.is-light.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-light.is-inverted.is-outlined.is-loading:focus::after,.button.is-light.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #f5f5f5 #f5f5f5 !important}.button.is-light.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark{background-color:#363636;border-color:transparent;color:#f5f5f5}.button.is-dark:hover,.button.is-dark.is-hovered{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.button.is-dark:focus,.button.is-dark.is-focused{border-color:transparent;color:#f5f5f5}.button.is-dark:focus:not(:active),.button.is-dark.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(54,54,54,0.25)}.button.is-dark:active,.button.is-dark.is-active{background-color:#292929;border-color:transparent;color:#f5f5f5}.button.is-dark[disabled],fieldset[disabled] .button.is-dark{background-color:#363636;border-color:transparent;box-shadow:none}.button.is-dark.is-inverted{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted:hover,.button.is-dark.is-inverted.is-hovered{background-color:#e8e8e8}.button.is-dark.is-inverted[disabled],fieldset[disabled] .button.is-dark.is-inverted{background-color:#f5f5f5;border-color:transparent;box-shadow:none;color:#363636}.button.is-dark.is-loading::after{border-color:transparent transparent #f5f5f5 #f5f5f5 !important}.button.is-dark.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-dark.is-outlined:hover,.button.is-dark.is-outlined.is-hovered,.button.is-dark.is-outlined:focus,.button.is-dark.is-outlined.is-focused{background-color:#363636;border-color:#363636;color:#f5f5f5}.button.is-dark.is-outlined.is-loading::after{border-color:transparent transparent #363636 #363636 !important}.button.is-dark.is-outlined.is-loading:hover::after,.button.is-dark.is-outlined.is-loading.is-hovered::after,.button.is-dark.is-outlined.is-loading:focus::after,.button.is-dark.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #f5f5f5 #f5f5f5 !important}.button.is-dark.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-outlined{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-dark.is-inverted.is-outlined:hover,.button.is-dark.is-inverted.is-outlined.is-hovered,.button.is-dark.is-inverted.is-outlined:focus,.button.is-dark.is-inverted.is-outlined.is-focused{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted.is-outlined.is-loading:hover::after,.button.is-dark.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-dark.is-inverted.is-outlined.is-loading:focus::after,.button.is-dark.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #363636 #363636 !important}.button.is-dark.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-primary{background-color:#00d1b2;border-color:transparent;color:#fff}.button.is-primary:hover,.button.is-primary.is-hovered{background-color:#00c4a7;border-color:transparent;color:#fff}.button.is-primary:focus,.button.is-primary.is-focused{border-color:transparent;color:#fff}.button.is-primary:focus:not(:active),.button.is-primary.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(0,209,178,0.25)}.button.is-primary:active,.button.is-primary.is-active{background-color:#00b89c;border-color:transparent;color:#fff}.button.is-primary[disabled],fieldset[disabled] .button.is-primary{background-color:#00d1b2;border-color:transparent;box-shadow:none}.button.is-primary.is-inverted{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted:hover,.button.is-primary.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-primary.is-inverted[disabled],fieldset[disabled] .button.is-primary.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#00d1b2}.button.is-primary.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;color:#00d1b2}.button.is-primary.is-outlined:hover,.button.is-primary.is-outlined.is-hovered,.button.is-primary.is-outlined:focus,.button.is-primary.is-outlined.is-focused{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.button.is-primary.is-outlined.is-loading::after{border-color:transparent transparent #00d1b2 #00d1b2 !important}.button.is-primary.is-outlined.is-loading:hover::after,.button.is-primary.is-outlined.is-loading.is-hovered::after,.button.is-primary.is-outlined.is-loading:focus::after,.button.is-primary.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-primary.is-outlined[disabled],fieldset[disabled] .button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;box-shadow:none;color:#00d1b2}.button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined:hover,.button.is-primary.is-inverted.is-outlined.is-hovered,.button.is-primary.is-inverted.is-outlined:focus,.button.is-primary.is-inverted.is-outlined.is-focused{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-outlined.is-loading:hover::after,.button.is-primary.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-primary.is-inverted.is-outlined.is-loading:focus::after,.button.is-primary.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #00d1b2 #00d1b2 !important}.button.is-primary.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-link{background-color:#3273dc;border-color:transparent;color:#fff}.button.is-link:hover,.button.is-link.is-hovered{background-color:#276cda;border-color:transparent;color:#fff}.button.is-link:focus,.button.is-link.is-focused{border-color:transparent;color:#fff}.button.is-link:focus:not(:active),.button.is-link.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(50,115,220,0.25)}.button.is-link:active,.button.is-link.is-active{background-color:#2366d1;border-color:transparent;color:#fff}.button.is-link[disabled],fieldset[disabled] .button.is-link{background-color:#3273dc;border-color:transparent;box-shadow:none}.button.is-link.is-inverted{background-color:#fff;color:#3273dc}.button.is-link.is-inverted:hover,.button.is-link.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-link.is-inverted[disabled],fieldset[disabled] .button.is-link.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#3273dc}.button.is-link.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-link.is-outlined{background-color:transparent;border-color:#3273dc;color:#3273dc}.button.is-link.is-outlined:hover,.button.is-link.is-outlined.is-hovered,.button.is-link.is-outlined:focus,.button.is-link.is-outlined.is-focused{background-color:#3273dc;border-color:#3273dc;color:#fff}.button.is-link.is-outlined.is-loading::after{border-color:transparent transparent #3273dc #3273dc !important}.button.is-link.is-outlined.is-loading:hover::after,.button.is-link.is-outlined.is-loading.is-hovered::after,.button.is-link.is-outlined.is-loading:focus::after,.button.is-link.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-link.is-outlined[disabled],fieldset[disabled] .button.is-link.is-outlined{background-color:transparent;border-color:#3273dc;box-shadow:none;color:#3273dc}.button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-link.is-inverted.is-outlined:hover,.button.is-link.is-inverted.is-outlined.is-hovered,.button.is-link.is-inverted.is-outlined:focus,.button.is-link.is-inverted.is-outlined.is-focused{background-color:#fff;color:#3273dc}.button.is-link.is-inverted.is-outlined.is-loading:hover::after,.button.is-link.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-link.is-inverted.is-outlined.is-loading:focus::after,.button.is-link.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #3273dc #3273dc !important}.button.is-link.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-info{background-color:#209cee;border-color:transparent;color:#fff}.button.is-info:hover,.button.is-info.is-hovered{background-color:#1496ed;border-color:transparent;color:#fff}.button.is-info:focus,.button.is-info.is-focused{border-color:transparent;color:#fff}.button.is-info:focus:not(:active),.button.is-info.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(32,156,238,0.25)}.button.is-info:active,.button.is-info.is-active{background-color:#118fe4;border-color:transparent;color:#fff}.button.is-info[disabled],fieldset[disabled] .button.is-info{background-color:#209cee;border-color:transparent;box-shadow:none}.button.is-info.is-inverted{background-color:#fff;color:#209cee}.button.is-info.is-inverted:hover,.button.is-info.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-info.is-inverted[disabled],fieldset[disabled] .button.is-info.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#209cee}.button.is-info.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-info.is-outlined{background-color:transparent;border-color:#209cee;color:#209cee}.button.is-info.is-outlined:hover,.button.is-info.is-outlined.is-hovered,.button.is-info.is-outlined:focus,.button.is-info.is-outlined.is-focused{background-color:#209cee;border-color:#209cee;color:#fff}.button.is-info.is-outlined.is-loading::after{border-color:transparent transparent #209cee #209cee !important}.button.is-info.is-outlined.is-loading:hover::after,.button.is-info.is-outlined.is-loading.is-hovered::after,.button.is-info.is-outlined.is-loading:focus::after,.button.is-info.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-info.is-outlined[disabled],fieldset[disabled] .button.is-info.is-outlined{background-color:transparent;border-color:#209cee;box-shadow:none;color:#209cee}.button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined:hover,.button.is-info.is-inverted.is-outlined.is-hovered,.button.is-info.is-inverted.is-outlined:focus,.button.is-info.is-inverted.is-outlined.is-focused{background-color:#fff;color:#209cee}.button.is-info.is-inverted.is-outlined.is-loading:hover::after,.button.is-info.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-info.is-inverted.is-outlined.is-loading:focus::after,.button.is-info.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #209cee #209cee !important}.button.is-info.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-success{background-color:#23d160;border-color:transparent;color:#fff}.button.is-success:hover,.button.is-success.is-hovered{background-color:#22c65b;border-color:transparent;color:#fff}.button.is-success:focus,.button.is-success.is-focused{border-color:transparent;color:#fff}.button.is-success:focus:not(:active),.button.is-success.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(35,209,96,0.25)}.button.is-success:active,.button.is-success.is-active{background-color:#20bc56;border-color:transparent;color:#fff}.button.is-success[disabled],fieldset[disabled] .button.is-success{background-color:#23d160;border-color:transparent;box-shadow:none}.button.is-success.is-inverted{background-color:#fff;color:#23d160}.button.is-success.is-inverted:hover,.button.is-success.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-success.is-inverted[disabled],fieldset[disabled] .button.is-success.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#23d160}.button.is-success.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-success.is-outlined{background-color:transparent;border-color:#23d160;color:#23d160}.button.is-success.is-outlined:hover,.button.is-success.is-outlined.is-hovered,.button.is-success.is-outlined:focus,.button.is-success.is-outlined.is-focused{background-color:#23d160;border-color:#23d160;color:#fff}.button.is-success.is-outlined.is-loading::after{border-color:transparent transparent #23d160 #23d160 !important}.button.is-success.is-outlined.is-loading:hover::after,.button.is-success.is-outlined.is-loading.is-hovered::after,.button.is-success.is-outlined.is-loading:focus::after,.button.is-success.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-success.is-outlined[disabled],fieldset[disabled] .button.is-success.is-outlined{background-color:transparent;border-color:#23d160;box-shadow:none;color:#23d160}.button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined:hover,.button.is-success.is-inverted.is-outlined.is-hovered,.button.is-success.is-inverted.is-outlined:focus,.button.is-success.is-inverted.is-outlined.is-focused{background-color:#fff;color:#23d160}.button.is-success.is-inverted.is-outlined.is-loading:hover::after,.button.is-success.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-success.is-inverted.is-outlined.is-loading:focus::after,.button.is-success.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #23d160 #23d160 !important}.button.is-success.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-warning{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-warning:hover,.button.is-warning.is-hovered{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-warning:focus,.button.is-warning.is-focused{border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-warning:focus:not(:active),.button.is-warning.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(255,221,87,0.25)}.button.is-warning:active,.button.is-warning.is-active{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-warning[disabled],fieldset[disabled] .button.is-warning{background-color:#ffdd57;border-color:transparent;box-shadow:none}.button.is-warning.is-inverted{background-color:rgba(0,0,0,0.7);color:#ffdd57}.button.is-warning.is-inverted:hover,.button.is-warning.is-inverted.is-hovered{background-color:rgba(0,0,0,0.7)}.button.is-warning.is-inverted[disabled],fieldset[disabled] .button.is-warning.is-inverted{background-color:rgba(0,0,0,0.7);border-color:transparent;box-shadow:none;color:#ffdd57}.button.is-warning.is-loading::after{border-color:transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important}.button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;color:#ffdd57}.button.is-warning.is-outlined:hover,.button.is-warning.is-outlined.is-hovered,.button.is-warning.is-outlined:focus,.button.is-warning.is-outlined.is-focused{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,0.7)}.button.is-warning.is-outlined.is-loading::after{border-color:transparent transparent #ffdd57 #ffdd57 !important}.button.is-warning.is-outlined.is-loading:hover::after,.button.is-warning.is-outlined.is-loading.is-hovered::after,.button.is-warning.is-outlined.is-loading:focus::after,.button.is-warning.is-outlined.is-loading.is-focused::after{border-color:transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important}.button.is-warning.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;box-shadow:none;color:#ffdd57}.button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,0.7);color:rgba(0,0,0,0.7)}.button.is-warning.is-inverted.is-outlined:hover,.button.is-warning.is-inverted.is-outlined.is-hovered,.button.is-warning.is-inverted.is-outlined:focus,.button.is-warning.is-inverted.is-outlined.is-focused{background-color:rgba(0,0,0,0.7);color:#ffdd57}.button.is-warning.is-inverted.is-outlined.is-loading:hover::after,.button.is-warning.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-warning.is-inverted.is-outlined.is-loading:focus::after,.button.is-warning.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #ffdd57 #ffdd57 !important}.button.is-warning.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,0.7);box-shadow:none;color:rgba(0,0,0,0.7)}.button.is-danger{background-color:#ff3860;border-color:transparent;color:#fff}.button.is-danger:hover,.button.is-danger.is-hovered{background-color:#ff2b56;border-color:transparent;color:#fff}.button.is-danger:focus,.button.is-danger.is-focused{border-color:transparent;color:#fff}.button.is-danger:focus:not(:active),.button.is-danger.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(255,56,96,0.25)}.button.is-danger:active,.button.is-danger.is-active{background-color:#ff1f4b;border-color:transparent;color:#fff}.button.is-danger[disabled],fieldset[disabled] .button.is-danger{background-color:#ff3860;border-color:transparent;box-shadow:none}.button.is-danger.is-inverted{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted:hover,.button.is-danger.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-danger.is-inverted[disabled],fieldset[disabled] .button.is-danger.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#ff3860}.button.is-danger.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-danger.is-outlined{background-color:transparent;border-color:#ff3860;color:#ff3860}.button.is-danger.is-outlined:hover,.button.is-danger.is-outlined.is-hovered,.button.is-danger.is-outlined:focus,.button.is-danger.is-outlined.is-focused{background-color:#ff3860;border-color:#ff3860;color:#fff}.button.is-danger.is-outlined.is-loading::after{border-color:transparent transparent #ff3860 #ff3860 !important}.button.is-danger.is-outlined.is-loading:hover::after,.button.is-danger.is-outlined.is-loading.is-hovered::after,.button.is-danger.is-outlined.is-loading:focus::after,.button.is-danger.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-danger.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-outlined{background-color:transparent;border-color:#ff3860;box-shadow:none;color:#ff3860}.button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined:hover,.button.is-danger.is-inverted.is-outlined.is-hovered,.button.is-danger.is-inverted.is-outlined:focus,.button.is-danger.is-inverted.is-outlined.is-focused{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted.is-outlined.is-loading:hover::after,.button.is-danger.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-danger.is-inverted.is-outlined.is-loading:focus::after,.button.is-danger.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #ff3860 #ff3860 !important}.button.is-danger.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-small{border-radius:2px;font-size:.75rem}.button.is-normal{font-size:1rem}.button.is-medium{font-size:1.25rem}.button.is-large{font-size:1.5rem}.button[disabled],fieldset[disabled] .button{background-color:#fff;border-color:#dbdbdb;box-shadow:none;opacity:.5}.button.is-fullwidth{display:flex;width:100%}.button.is-loading{color:transparent !important;pointer-events:none}.button.is-loading::after{position:absolute;left:calc(50% - (1em / 2));top:calc(50% - (1em / 2));position:absolute !important}.button.is-static{background-color:#f5f5f5;border-color:#dbdbdb;color:#7a7a7a;box-shadow:none;pointer-events:none}.button.is-rounded{border-radius:290486px;padding-left:1em;padding-right:1em}.buttons{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.buttons .button{margin-bottom:0.5rem}.buttons .button:not(:last-child):not(.is-fullwidth){margin-right:0.5rem}.buttons:last-child{margin-bottom:-0.5rem}.buttons:not(:last-child){margin-bottom:1rem}.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large){border-radius:2px;font-size:.75rem}.buttons.are-medium .button:not(.is-small):not(.is-normal):not(.is-large){font-size:1.25rem}.buttons.are-large .button:not(.is-small):not(.is-normal):not(.is-medium){font-size:1.5rem}.buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.buttons.has-addons .button:last-child{margin-right:0}.buttons.has-addons .button:hover,.buttons.has-addons .button.is-hovered{z-index:2}.buttons.has-addons .button:focus,.buttons.has-addons .button.is-focused,.buttons.has-addons .button:active,.buttons.has-addons .button.is-active,.buttons.has-addons .button.is-selected{z-index:3}.buttons.has-addons .button:focus:hover,.buttons.has-addons .button.is-focused:hover,.buttons.has-addons .button:active:hover,.buttons.has-addons .button.is-active:hover,.buttons.has-addons .button.is-selected:hover{z-index:4}.buttons.has-addons .button.is-expanded{flex-grow:1;flex-shrink:1}.buttons.is-centered{justify-content:center}.buttons.is-centered:not(.has-addons) .button:not(.is-fullwidth){margin-left:0.25rem;margin-right:0.25rem}.buttons.is-right{justify-content:flex-end}.buttons.is-right:not(.has-addons) .button:not(.is-fullwidth){margin-left:0.25rem;margin-right:0.25rem}.container{flex-grow:1;margin:0 auto;position:relative;width:auto}@media screen and (min-width: 1024px){.container{max-width:960px}.container.is-fluid{margin-left:32px;margin-right:32px;max-width:none}}@media screen and (max-width: 1215px){.container.is-widescreen{max-width:1152px}}@media screen and (max-width: 1407px){.container.is-fullhd{max-width:1344px}}@media screen and (min-width: 1216px){.container{max-width:1152px}}@media screen and (min-width: 1408px){.container{max-width:1344px}}.content li+li{margin-top:0.25em}.content p:not(:last-child),.content dl:not(:last-child),.content ol:not(:last-child),.content ul:not(:last-child),.content blockquote:not(:last-child),.content pre:not(:last-child),.content table:not(:last-child){margin-bottom:1em}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#363636;font-weight:600;line-height:1.125}.content h1{font-size:2em;margin-bottom:0.5em}.content h1:not(:first-child){margin-top:1em}.content h2{font-size:1.75em;margin-bottom:0.5714em}.content h2:not(:first-child){margin-top:1.1428em}.content h3{font-size:1.5em;margin-bottom:0.6666em}.content h3:not(:first-child){margin-top:1.3333em}.content h4{font-size:1.25em;margin-bottom:0.8em}.content h5{font-size:1.125em;margin-bottom:0.8888em}.content h6{font-size:1em;margin-bottom:1em}.content blockquote{background-color:#f5f5f5;border-left:5px solid #dbdbdb;padding:1.25em 1.5em}.content ol{list-style-position:outside;margin-left:2em;margin-top:1em}.content ol:not([type]){list-style-type:decimal}.content ol:not([type]).is-lower-alpha{list-style-type:lower-alpha}.content ol:not([type]).is-lower-roman{list-style-type:lower-roman}.content ol:not([type]).is-upper-alpha{list-style-type:upper-alpha}.content ol:not([type]).is-upper-roman{list-style-type:upper-roman}.content ul{list-style:disc outside;margin-left:2em;margin-top:1em}.content ul ul{list-style-type:circle;margin-top:0.5em}.content ul ul ul{list-style-type:square}.content dd{margin-left:2em}.content figure{margin-left:2em;margin-right:2em;text-align:center}.content figure:not(:first-child){margin-top:2em}.content figure:not(:last-child){margin-bottom:2em}.content figure img{display:inline-block}.content figure figcaption{font-style:italic}.content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:1.25em 1.5em;white-space:pre;word-wrap:normal}.content sup,.content sub{font-size:75%}.content table{width:100%}.content table td,.content table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:0.5em 0.75em;vertical-align:top}.content table th{color:#363636}.content table th:not([align]){text-align:left}.content table thead td,.content table thead th{border-width:0 0 2px;color:#363636}.content table tfoot td,.content table tfoot th{border-width:2px 0 0;color:#363636}.content table tbody tr:last-child td,.content table tbody tr:last-child th{border-bottom-width:0}.content .tabs li+li{margin-top:0}.content.is-small{font-size:.75rem}.content.is-medium{font-size:1.25rem}.content.is-large{font-size:1.5rem}.icon{align-items:center;display:inline-flex;justify-content:center;height:1.5rem;width:1.5rem}.icon.is-small{height:1rem;width:1rem}.icon.is-medium{height:2rem;width:2rem}.icon.is-large{height:3rem;width:3rem}.image{display:block;position:relative}.image img{display:block;height:auto;width:100%}.image img.is-rounded{border-radius:290486px}.image.is-square img,.image.is-square .has-ratio,.image.is-1by1 img,.image.is-1by1 .has-ratio,.image.is-5by4 img,.image.is-5by4 .has-ratio,.image.is-4by3 img,.image.is-4by3 .has-ratio,.image.is-3by2 img,.image.is-3by2 .has-ratio,.image.is-5by3 img,.image.is-5by3 .has-ratio,.image.is-16by9 img,.image.is-16by9 .has-ratio,.image.is-2by1 img,.image.is-2by1 .has-ratio,.image.is-3by1 img,.image.is-3by1 .has-ratio,.image.is-4by5 img,.image.is-4by5 .has-ratio,.image.is-3by4 img,.image.is-3by4 .has-ratio,.image.is-2by3 img,.image.is-2by3 .has-ratio,.image.is-3by5 img,.image.is-3by5 .has-ratio,.image.is-9by16 img,.image.is-9by16 .has-ratio,.image.is-1by2 img,.image.is-1by2 .has-ratio,.image.is-1by3 img,.image.is-1by3 .has-ratio{height:100%;width:100%}.image.is-square,.image.is-1by1{padding-top:100%}.image.is-5by4{padding-top:80%}.image.is-4by3{padding-top:75%}.image.is-3by2{padding-top:66.6666%}.image.is-5by3{padding-top:60%}.image.is-16by9{padding-top:56.25%}.image.is-2by1{padding-top:50%}.image.is-3by1{padding-top:33.3333%}.image.is-4by5{padding-top:125%}.image.is-3by4{padding-top:133.3333%}.image.is-2by3{padding-top:150%}.image.is-3by5{padding-top:166.6666%}.image.is-9by16{padding-top:177.7777%}.image.is-1by2{padding-top:200%}.image.is-1by3{padding-top:300%}.image.is-16x16{height:16px;width:16px}.image.is-24x24{height:24px;width:24px}.image.is-32x32{height:32px;width:32px}.image.is-48x48{height:48px;width:48px}.image.is-64x64{height:64px;width:64px}.image.is-96x96{height:96px;width:96px}.image.is-128x128{height:128px;width:128px}.notification{background-color:#f5f5f5;border-radius:4px;padding:1.25rem 2.5rem 1.25rem 1.5rem;position:relative}.notification a:not(.button):not(.dropdown-item){color:currentColor;text-decoration:underline}.notification strong{color:currentColor}.notification code,.notification pre{background:#fff}.notification pre code{background:transparent}.notification>.delete{position:absolute;right:0.5rem;top:0.5rem}.notification .title,.notification .subtitle,.notification .content{color:currentColor}.notification.is-white{background-color:#fff;color:#0a0a0a}.notification.is-black{background-color:#0a0a0a;color:#fff}.notification.is-light{background-color:#f5f5f5;color:#363636}.notification.is-dark{background-color:#363636;color:#f5f5f5}.notification.is-primary{background-color:#00d1b2;color:#fff}.notification.is-link{background-color:#3273dc;color:#fff}.notification.is-info{background-color:#209cee;color:#fff}.notification.is-success{background-color:#23d160;color:#fff}.notification.is-warning{background-color:#ffdd57;color:rgba(0,0,0,0.7)}.notification.is-danger{background-color:#ff3860;color:#fff}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:290486px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}.progress::-webkit-progress-bar{background-color:#dbdbdb}.progress::-webkit-progress-value{background-color:#4a4a4a}.progress::-moz-progress-bar{background-color:#4a4a4a}.progress::-ms-fill{background-color:#4a4a4a;border:none}.progress.is-white::-webkit-progress-value{background-color:#fff}.progress.is-white::-moz-progress-bar{background-color:#fff}.progress.is-white::-ms-fill{background-color:#fff}.progress.is-white:indeterminate{background-image:linear-gradient(to right, #fff 30%, #dbdbdb 30%)}.progress.is-black::-webkit-progress-value{background-color:#0a0a0a}.progress.is-black::-moz-progress-bar{background-color:#0a0a0a}.progress.is-black::-ms-fill{background-color:#0a0a0a}.progress.is-black:indeterminate{background-image:linear-gradient(to right, #0a0a0a 30%, #dbdbdb 30%)}.progress.is-light::-webkit-progress-value{background-color:#f5f5f5}.progress.is-light::-moz-progress-bar{background-color:#f5f5f5}.progress.is-light::-ms-fill{background-color:#f5f5f5}.progress.is-light:indeterminate{background-image:linear-gradient(to right, #f5f5f5 30%, #dbdbdb 30%)}.progress.is-dark::-webkit-progress-value{background-color:#363636}.progress.is-dark::-moz-progress-bar{background-color:#363636}.progress.is-dark::-ms-fill{background-color:#363636}.progress.is-dark:indeterminate{background-image:linear-gradient(to right, #363636 30%, #dbdbdb 30%)}.progress.is-primary::-webkit-progress-value{background-color:#00d1b2}.progress.is-primary::-moz-progress-bar{background-color:#00d1b2}.progress.is-primary::-ms-fill{background-color:#00d1b2}.progress.is-primary:indeterminate{background-image:linear-gradient(to right, #00d1b2 30%, #dbdbdb 30%)}.progress.is-link::-webkit-progress-value{background-color:#3273dc}.progress.is-link::-moz-progress-bar{background-color:#3273dc}.progress.is-link::-ms-fill{background-color:#3273dc}.progress.is-link:indeterminate{background-image:linear-gradient(to right, #3273dc 30%, #dbdbdb 30%)}.progress.is-info::-webkit-progress-value{background-color:#209cee}.progress.is-info::-moz-progress-bar{background-color:#209cee}.progress.is-info::-ms-fill{background-color:#209cee}.progress.is-info:indeterminate{background-image:linear-gradient(to right, #209cee 30%, #dbdbdb 30%)}.progress.is-success::-webkit-progress-value{background-color:#23d160}.progress.is-success::-moz-progress-bar{background-color:#23d160}.progress.is-success::-ms-fill{background-color:#23d160}.progress.is-success:indeterminate{background-image:linear-gradient(to right, #23d160 30%, #dbdbdb 30%)}.progress.is-warning::-webkit-progress-value{background-color:#ffdd57}.progress.is-warning::-moz-progress-bar{background-color:#ffdd57}.progress.is-warning::-ms-fill{background-color:#ffdd57}.progress.is-warning:indeterminate{background-image:linear-gradient(to right, #ffdd57 30%, #dbdbdb 30%)}.progress.is-danger::-webkit-progress-value{background-color:#ff3860}.progress.is-danger::-moz-progress-bar{background-color:#ff3860}.progress.is-danger::-ms-fill{background-color:#ff3860}.progress.is-danger:indeterminate{background-image:linear-gradient(to right, #ff3860 30%, #dbdbdb 30%)}.progress:indeterminate{-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:moveIndeterminate;animation-name:moveIndeterminate;-webkit-animation-timing-function:linear;animation-timing-function:linear;background-color:#dbdbdb;background-image:linear-gradient(to right, #4a4a4a 30%, #dbdbdb 30%);background-position:top left;background-repeat:no-repeat;background-size:150% 150%}.progress:indeterminate::-webkit-progress-bar{background-color:transparent}.progress:indeterminate::-moz-progress-bar{background-color:transparent}.progress.is-small{height:.75rem}.progress.is-medium{height:1.25rem}.progress.is-large{height:1.5rem}@-webkit-keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}@keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}.table{background-color:#fff;color:#363636}.table td,.table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:0.5em 0.75em;vertical-align:top}.table td.is-white,.table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}.table td.is-black,.table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.table td.is-light,.table th.is-light{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.table td.is-dark,.table th.is-dark{background-color:#363636;border-color:#363636;color:#f5f5f5}.table td.is-primary,.table th.is-primary{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.table td.is-link,.table th.is-link{background-color:#3273dc;border-color:#3273dc;color:#fff}.table td.is-info,.table th.is-info{background-color:#209cee;border-color:#209cee;color:#fff}.table td.is-success,.table th.is-success{background-color:#23d160;border-color:#23d160;color:#fff}.table td.is-warning,.table th.is-warning{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,0.7)}.table td.is-danger,.table th.is-danger{background-color:#ff3860;border-color:#ff3860;color:#fff}.table td.is-narrow,.table th.is-narrow{white-space:nowrap;width:1%}.table td.is-selected,.table th.is-selected{background-color:#00d1b2;color:#fff}.table td.is-selected a,.table td.is-selected strong,.table th.is-selected a,.table th.is-selected strong{color:currentColor}.table th{color:#363636}.table th:not([align]){text-align:left}.table tr.is-selected{background-color:#00d1b2;color:#fff}.table tr.is-selected a,.table tr.is-selected strong{color:currentColor}.table tr.is-selected td,.table tr.is-selected th{border-color:#fff;color:currentColor}.table thead{background-color:rgba(0,0,0,0)}.table thead td,.table thead th{border-width:0 0 2px;color:#363636}.table tfoot{background-color:rgba(0,0,0,0)}.table tfoot td,.table tfoot th{border-width:2px 0 0;color:#363636}.table tbody{background-color:rgba(0,0,0,0)}.table tbody tr:last-child td,.table tbody tr:last-child th{border-bottom-width:0}.table.is-bordered td,.table.is-bordered th{border-width:1px}.table.is-bordered tr:last-child td,.table.is-bordered tr:last-child th{border-bottom-width:1px}.table.is-fullwidth{width:100%}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(even){background-color:#f5f5f5}.table.is-narrow td,.table.is-narrow th{padding:0.25em 0.5em}.table.is-striped tbody tr:not(.is-selected):nth-child(even){background-color:#fafafa}.table-container{-webkit-overflow-scrolling:touch;overflow:auto;overflow-y:hidden;max-width:100%}.tags{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.tags .tag{margin-bottom:0.5rem}.tags .tag:not(:last-child){margin-right:0.5rem}.tags:last-child{margin-bottom:-0.5rem}.tags:not(:last-child){margin-bottom:1rem}.tags.are-medium .tag:not(.is-normal):not(.is-large){font-size:1rem}.tags.are-large .tag:not(.is-normal):not(.is-medium){font-size:1.25rem}.tags.is-centered{justify-content:center}.tags.is-centered .tag{margin-right:0.25rem;margin-left:0.25rem}.tags.is-right{justify-content:flex-end}.tags.is-right .tag:not(:first-child){margin-left:0.5rem}.tags.is-right .tag:not(:last-child){margin-right:0}.tags.has-addons .tag{margin-right:0}.tags.has-addons .tag:not(:first-child){margin-left:0;border-bottom-left-radius:0;border-top-left-radius:0}.tags.has-addons .tag:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.tag:not(body){align-items:center;background-color:#f5f5f5;border-radius:4px;color:#4a4a4a;display:inline-flex;font-size:.75rem;height:2em;justify-content:center;line-height:1.5;padding-left:0.75em;padding-right:0.75em;white-space:nowrap}.tag:not(body) .delete{margin-left:0.25rem;margin-right:-0.375rem}.tag:not(body).is-white{background-color:#fff;color:#0a0a0a}.tag:not(body).is-black{background-color:#0a0a0a;color:#fff}.tag:not(body).is-light{background-color:#f5f5f5;color:#363636}.tag:not(body).is-dark{background-color:#363636;color:#f5f5f5}.tag:not(body).is-primary{background-color:#00d1b2;color:#fff}.tag:not(body).is-link{background-color:#3273dc;color:#fff}.tag:not(body).is-info{background-color:#209cee;color:#fff}.tag:not(body).is-success{background-color:#23d160;color:#fff}.tag:not(body).is-warning{background-color:#ffdd57;color:rgba(0,0,0,0.7)}.tag:not(body).is-danger{background-color:#ff3860;color:#fff}.tag:not(body).is-normal{font-size:.75rem}.tag:not(body).is-medium{font-size:1rem}.tag:not(body).is-large{font-size:1.25rem}.tag:not(body) .icon:first-child:not(:last-child){margin-left:-0.375em;margin-right:0.1875em}.tag:not(body) .icon:last-child:not(:first-child){margin-left:0.1875em;margin-right:-0.375em}.tag:not(body) .icon:first-child:last-child{margin-left:-0.375em;margin-right:-0.375em}.tag:not(body).is-delete{margin-left:1px;padding:0;position:relative;width:2em}.tag:not(body).is-delete::before,.tag:not(body).is-delete::after{background-color:currentColor;content:"";display:block;left:50%;position:absolute;top:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);-webkit-transform-origin:center center;transform-origin:center center}.tag:not(body).is-delete::before{height:1px;width:50%}.tag:not(body).is-delete::after{height:50%;width:1px}.tag:not(body).is-delete:hover,.tag:not(body).is-delete:focus{background-color:#e8e8e8}.tag:not(body).is-delete:active{background-color:#dbdbdb}.tag:not(body).is-rounded{border-radius:290486px}a.tag:hover{text-decoration:underline}.title,.subtitle{word-break:break-word}.title em,.title span,.subtitle em,.subtitle span{font-weight:inherit}.title sub,.subtitle sub{font-size:.75em}.title sup,.subtitle sup{font-size:.75em}.title .tag,.subtitle .tag{vertical-align:middle}.title{color:#363636;font-size:2rem;font-weight:600;line-height:1.125}.title strong{color:inherit;font-weight:inherit}.title+.highlight{margin-top:-0.75rem}.title:not(.is-spaced)+.subtitle{margin-top:-1.25rem}.title.is-1{font-size:3rem}.title.is-2{font-size:2.5rem}.title.is-3{font-size:2rem}.title.is-4{font-size:1.5rem}.title.is-5{font-size:1.25rem}.title.is-6{font-size:1rem}.title.is-7{font-size:.75rem}.subtitle{color:#4a4a4a;font-size:1.25rem;font-weight:400;line-height:1.25}.subtitle strong{color:#363636;font-weight:600}.subtitle:not(.is-spaced)+.title{margin-top:-1.25rem}.subtitle.is-1{font-size:3rem}.subtitle.is-2{font-size:2.5rem}.subtitle.is-3{font-size:2rem}.subtitle.is-4{font-size:1.5rem}.subtitle.is-5{font-size:1.25rem}.subtitle.is-6{font-size:1rem}.subtitle.is-7{font-size:.75rem}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.highlight{font-weight:400;max-width:100%;overflow:hidden;padding:0}.highlight pre{overflow:auto;max-width:100%}.number{align-items:center;background-color:#f5f5f5;border-radius:290486px;display:inline-flex;font-size:1.25rem;height:2em;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:0.25rem 0.5rem;text-align:center;vertical-align:top}.input,.textarea,.select select{background-color:#fff;border-color:#dbdbdb;border-radius:4px;color:#363636}.input::-moz-placeholder,.textarea::-moz-placeholder,.select select::-moz-placeholder{color:rgba(54,54,54,0.3)}.input::-webkit-input-placeholder,.textarea::-webkit-input-placeholder,.select select::-webkit-input-placeholder{color:rgba(54,54,54,0.3)}.input:-moz-placeholder,.textarea:-moz-placeholder,.select select:-moz-placeholder{color:rgba(54,54,54,0.3)}.input:-ms-input-placeholder,.textarea:-ms-input-placeholder,.select select:-ms-input-placeholder{color:rgba(54,54,54,0.3)}.input:hover,.textarea:hover,.select select:hover,.is-hovered.input,.is-hovered.textarea,.select select.is-hovered{border-color:#b5b5b5}.input:focus,.textarea:focus,.select select:focus,.is-focused.input,.is-focused.textarea,.select select.is-focused,.input:active,.textarea:active,.select select:active,.is-active.input,.is-active.textarea,.select select.is-active{border-color:#3273dc;box-shadow:0 0 0 0.125em rgba(50,115,220,0.25)}.input[disabled],.textarea[disabled],.select select[disabled],fieldset[disabled] .input,fieldset[disabled] .textarea,fieldset[disabled] .select select,.select fieldset[disabled] select{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.input[disabled]::-moz-placeholder,.textarea[disabled]::-moz-placeholder,.select select[disabled]::-moz-placeholder,fieldset[disabled] .input::-moz-placeholder,fieldset[disabled] .textarea::-moz-placeholder,fieldset[disabled] .select select::-moz-placeholder,.select fieldset[disabled] select::-moz-placeholder{color:rgba(122,122,122,0.3)}.input[disabled]::-webkit-input-placeholder,.textarea[disabled]::-webkit-input-placeholder,.select select[disabled]::-webkit-input-placeholder,fieldset[disabled] .input::-webkit-input-placeholder,fieldset[disabled] .textarea::-webkit-input-placeholder,fieldset[disabled] .select select::-webkit-input-placeholder,.select fieldset[disabled] select::-webkit-input-placeholder{color:rgba(122,122,122,0.3)}.input[disabled]:-moz-placeholder,.textarea[disabled]:-moz-placeholder,.select select[disabled]:-moz-placeholder,fieldset[disabled] .input:-moz-placeholder,fieldset[disabled] .textarea:-moz-placeholder,fieldset[disabled] .select select:-moz-placeholder,.select fieldset[disabled] select:-moz-placeholder{color:rgba(122,122,122,0.3)}.input[disabled]:-ms-input-placeholder,.textarea[disabled]:-ms-input-placeholder,.select select[disabled]:-ms-input-placeholder,fieldset[disabled] .input:-ms-input-placeholder,fieldset[disabled] .textarea:-ms-input-placeholder,fieldset[disabled] .select select:-ms-input-placeholder,.select fieldset[disabled] select:-ms-input-placeholder{color:rgba(122,122,122,0.3)}.input,.textarea{box-shadow:inset 0 1px 2px rgba(10,10,10,0.1);max-width:100%;width:100%}.input[readonly],.textarea[readonly]{box-shadow:none}.is-white.input,.is-white.textarea{border-color:#fff}.is-white.input:focus,.is-white.textarea:focus,.is-white.is-focused.input,.is-white.is-focused.textarea,.is-white.input:active,.is-white.textarea:active,.is-white.is-active.input,.is-white.is-active.textarea{box-shadow:0 0 0 0.125em rgba(255,255,255,0.25)}.is-black.input,.is-black.textarea{border-color:#0a0a0a}.is-black.input:focus,.is-black.textarea:focus,.is-black.is-focused.input,.is-black.is-focused.textarea,.is-black.input:active,.is-black.textarea:active,.is-black.is-active.input,.is-black.is-active.textarea{box-shadow:0 0 0 0.125em rgba(10,10,10,0.25)}.is-light.input,.is-light.textarea{border-color:#f5f5f5}.is-light.input:focus,.is-light.textarea:focus,.is-light.is-focused.input,.is-light.is-focused.textarea,.is-light.input:active,.is-light.textarea:active,.is-light.is-active.input,.is-light.is-active.textarea{box-shadow:0 0 0 0.125em rgba(245,245,245,0.25)}.is-dark.input,.is-dark.textarea{border-color:#363636}.is-dark.input:focus,.is-dark.textarea:focus,.is-dark.is-focused.input,.is-dark.is-focused.textarea,.is-dark.input:active,.is-dark.textarea:active,.is-dark.is-active.input,.is-dark.is-active.textarea{box-shadow:0 0 0 0.125em rgba(54,54,54,0.25)}.is-primary.input,.is-primary.textarea{border-color:#00d1b2}.is-primary.input:focus,.is-primary.textarea:focus,.is-primary.is-focused.input,.is-primary.is-focused.textarea,.is-primary.input:active,.is-primary.textarea:active,.is-primary.is-active.input,.is-primary.is-active.textarea{box-shadow:0 0 0 0.125em rgba(0,209,178,0.25)}.is-link.input,.is-link.textarea{border-color:#3273dc}.is-link.input:focus,.is-link.textarea:focus,.is-link.is-focused.input,.is-link.is-focused.textarea,.is-link.input:active,.is-link.textarea:active,.is-link.is-active.input,.is-link.is-active.textarea{box-shadow:0 0 0 0.125em rgba(50,115,220,0.25)}.is-info.input,.is-info.textarea{border-color:#209cee}.is-info.input:focus,.is-info.textarea:focus,.is-info.is-focused.input,.is-info.is-focused.textarea,.is-info.input:active,.is-info.textarea:active,.is-info.is-active.input,.is-info.is-active.textarea{box-shadow:0 0 0 0.125em rgba(32,156,238,0.25)}.is-success.input,.is-success.textarea{border-color:#23d160}.is-success.input:focus,.is-success.textarea:focus,.is-success.is-focused.input,.is-success.is-focused.textarea,.is-success.input:active,.is-success.textarea:active,.is-success.is-active.input,.is-success.is-active.textarea{box-shadow:0 0 0 0.125em rgba(35,209,96,0.25)}.is-warning.input,.is-warning.textarea{border-color:#ffdd57}.is-warning.input:focus,.is-warning.textarea:focus,.is-warning.is-focused.input,.is-warning.is-focused.textarea,.is-warning.input:active,.is-warning.textarea:active,.is-warning.is-active.input,.is-warning.is-active.textarea{box-shadow:0 0 0 0.125em rgba(255,221,87,0.25)}.is-danger.input,.is-danger.textarea{border-color:#ff3860}.is-danger.input:focus,.is-danger.textarea:focus,.is-danger.is-focused.input,.is-danger.is-focused.textarea,.is-danger.input:active,.is-danger.textarea:active,.is-danger.is-active.input,.is-danger.is-active.textarea{box-shadow:0 0 0 0.125em rgba(255,56,96,0.25)}.is-small.input,.is-small.textarea{border-radius:2px;font-size:.75rem}.is-medium.input,.is-medium.textarea{font-size:1.25rem}.is-large.input,.is-large.textarea{font-size:1.5rem}.is-fullwidth.input,.is-fullwidth.textarea{display:block;width:100%}.is-inline.input,.is-inline.textarea{display:inline;width:auto}.input.is-rounded{border-radius:290486px;padding-left:1em;padding-right:1em}.input.is-static{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.textarea{display:block;max-width:100%;min-width:100%;padding:0.625em;resize:vertical}.textarea:not([rows]){max-height:600px;min-height:120px}.textarea[rows]{height:initial}.textarea.has-fixed-size{resize:none}.checkbox,.radio{cursor:pointer;display:inline-block;line-height:1.25;position:relative}.checkbox input,.radio input{cursor:pointer}.checkbox:hover,.radio:hover{color:#363636}.checkbox[disabled],.radio[disabled],fieldset[disabled] .checkbox,fieldset[disabled] .radio{color:#7a7a7a;cursor:not-allowed}.radio+.radio{margin-left:0.5em}.select{display:inline-block;max-width:100%;position:relative;vertical-align:top}.select:not(.is-multiple){height:2.25em}.select:not(.is-multiple):not(.is-loading)::after{border-color:#3273dc;right:1.125em;z-index:4}.select.is-rounded select{border-radius:290486px;padding-left:1em}.select select{cursor:pointer;display:block;font-size:1em;max-width:100%;outline:none}.select select::-ms-expand{display:none}.select select[disabled]:hover,fieldset[disabled] .select select:hover{border-color:#f5f5f5}.select select:not([multiple]){padding-right:2.5em}.select select[multiple]{height:auto;padding:0}.select select[multiple] option{padding:0.5em 1em}.select:not(.is-multiple):not(.is-loading):hover::after{border-color:#363636}.select.is-white:not(:hover)::after{border-color:#fff}.select.is-white select{border-color:#fff}.select.is-white select:hover,.select.is-white select.is-hovered{border-color:#f2f2f2}.select.is-white select:focus,.select.is-white select.is-focused,.select.is-white select:active,.select.is-white select.is-active{box-shadow:0 0 0 0.125em rgba(255,255,255,0.25)}.select.is-black:not(:hover)::after{border-color:#0a0a0a}.select.is-black select{border-color:#0a0a0a}.select.is-black select:hover,.select.is-black select.is-hovered{border-color:#000}.select.is-black select:focus,.select.is-black select.is-focused,.select.is-black select:active,.select.is-black select.is-active{box-shadow:0 0 0 0.125em rgba(10,10,10,0.25)}.select.is-light:not(:hover)::after{border-color:#f5f5f5}.select.is-light select{border-color:#f5f5f5}.select.is-light select:hover,.select.is-light select.is-hovered{border-color:#e8e8e8}.select.is-light select:focus,.select.is-light select.is-focused,.select.is-light select:active,.select.is-light select.is-active{box-shadow:0 0 0 0.125em rgba(245,245,245,0.25)}.select.is-dark:not(:hover)::after{border-color:#363636}.select.is-dark select{border-color:#363636}.select.is-dark select:hover,.select.is-dark select.is-hovered{border-color:#292929}.select.is-dark select:focus,.select.is-dark select.is-focused,.select.is-dark select:active,.select.is-dark select.is-active{box-shadow:0 0 0 0.125em rgba(54,54,54,0.25)}.select.is-primary:not(:hover)::after{border-color:#00d1b2}.select.is-primary select{border-color:#00d1b2}.select.is-primary select:hover,.select.is-primary select.is-hovered{border-color:#00b89c}.select.is-primary select:focus,.select.is-primary select.is-focused,.select.is-primary select:active,.select.is-primary select.is-active{box-shadow:0 0 0 0.125em rgba(0,209,178,0.25)}.select.is-link:not(:hover)::after{border-color:#3273dc}.select.is-link select{border-color:#3273dc}.select.is-link select:hover,.select.is-link select.is-hovered{border-color:#2366d1}.select.is-link select:focus,.select.is-link select.is-focused,.select.is-link select:active,.select.is-link select.is-active{box-shadow:0 0 0 0.125em rgba(50,115,220,0.25)}.select.is-info:not(:hover)::after{border-color:#209cee}.select.is-info select{border-color:#209cee}.select.is-info select:hover,.select.is-info select.is-hovered{border-color:#118fe4}.select.is-info select:focus,.select.is-info select.is-focused,.select.is-info select:active,.select.is-info select.is-active{box-shadow:0 0 0 0.125em rgba(32,156,238,0.25)}.select.is-success:not(:hover)::after{border-color:#23d160}.select.is-success select{border-color:#23d160}.select.is-success select:hover,.select.is-success select.is-hovered{border-color:#20bc56}.select.is-success select:focus,.select.is-success select.is-focused,.select.is-success select:active,.select.is-success select.is-active{box-shadow:0 0 0 0.125em rgba(35,209,96,0.25)}.select.is-warning:not(:hover)::after{border-color:#ffdd57}.select.is-warning select{border-color:#ffdd57}.select.is-warning select:hover,.select.is-warning select.is-hovered{border-color:#ffd83d}.select.is-warning select:focus,.select.is-warning select.is-focused,.select.is-warning select:active,.select.is-warning select.is-active{box-shadow:0 0 0 0.125em rgba(255,221,87,0.25)}.select.is-danger:not(:hover)::after{border-color:#ff3860}.select.is-danger select{border-color:#ff3860}.select.is-danger select:hover,.select.is-danger select.is-hovered{border-color:#ff1f4b}.select.is-danger select:focus,.select.is-danger select.is-focused,.select.is-danger select:active,.select.is-danger select.is-active{box-shadow:0 0 0 0.125em rgba(255,56,96,0.25)}.select.is-small{border-radius:2px;font-size:.75rem}.select.is-medium{font-size:1.25rem}.select.is-large{font-size:1.5rem}.select.is-disabled::after{border-color:#7a7a7a}.select.is-fullwidth{width:100%}.select.is-fullwidth select{width:100%}.select.is-loading::after{margin-top:0;position:absolute;right:0.625em;top:0.625em;-webkit-transform:none;transform:none}.select.is-loading.is-small:after{font-size:.75rem}.select.is-loading.is-medium:after{font-size:1.25rem}.select.is-loading.is-large:after{font-size:1.5rem}.file{align-items:stretch;display:flex;justify-content:flex-start;position:relative}.file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}.file.is-white:hover .file-cta,.file.is-white.is-hovered .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.file.is-white:focus .file-cta,.file.is-white.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(255,255,255,0.25);color:#0a0a0a}.file.is-white:active .file-cta,.file.is-white.is-active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}.file.is-black:hover .file-cta,.file.is-black.is-hovered .file-cta{background-color:#040404;border-color:transparent;color:#fff}.file.is-black:focus .file-cta,.file.is-black.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(10,10,10,0.25);color:#fff}.file.is-black:active .file-cta,.file.is-black.is-active .file-cta{background-color:#000;border-color:transparent;color:#fff}.file.is-light .file-cta{background-color:#f5f5f5;border-color:transparent;color:#363636}.file.is-light:hover .file-cta,.file.is-light.is-hovered .file-cta{background-color:#eee;border-color:transparent;color:#363636}.file.is-light:focus .file-cta,.file.is-light.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(245,245,245,0.25);color:#363636}.file.is-light:active .file-cta,.file.is-light.is-active .file-cta{background-color:#e8e8e8;border-color:transparent;color:#363636}.file.is-dark .file-cta{background-color:#363636;border-color:transparent;color:#f5f5f5}.file.is-dark:hover .file-cta,.file.is-dark.is-hovered .file-cta{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.file.is-dark:focus .file-cta,.file.is-dark.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(54,54,54,0.25);color:#f5f5f5}.file.is-dark:active .file-cta,.file.is-dark.is-active .file-cta{background-color:#292929;border-color:transparent;color:#f5f5f5}.file.is-primary .file-cta{background-color:#00d1b2;border-color:transparent;color:#fff}.file.is-primary:hover .file-cta,.file.is-primary.is-hovered .file-cta{background-color:#00c4a7;border-color:transparent;color:#fff}.file.is-primary:focus .file-cta,.file.is-primary.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(0,209,178,0.25);color:#fff}.file.is-primary:active .file-cta,.file.is-primary.is-active .file-cta{background-color:#00b89c;border-color:transparent;color:#fff}.file.is-link .file-cta{background-color:#3273dc;border-color:transparent;color:#fff}.file.is-link:hover .file-cta,.file.is-link.is-hovered .file-cta{background-color:#276cda;border-color:transparent;color:#fff}.file.is-link:focus .file-cta,.file.is-link.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(50,115,220,0.25);color:#fff}.file.is-link:active .file-cta,.file.is-link.is-active .file-cta{background-color:#2366d1;border-color:transparent;color:#fff}.file.is-info .file-cta{background-color:#209cee;border-color:transparent;color:#fff}.file.is-info:hover .file-cta,.file.is-info.is-hovered .file-cta{background-color:#1496ed;border-color:transparent;color:#fff}.file.is-info:focus .file-cta,.file.is-info.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(32,156,238,0.25);color:#fff}.file.is-info:active .file-cta,.file.is-info.is-active .file-cta{background-color:#118fe4;border-color:transparent;color:#fff}.file.is-success .file-cta{background-color:#23d160;border-color:transparent;color:#fff}.file.is-success:hover .file-cta,.file.is-success.is-hovered .file-cta{background-color:#22c65b;border-color:transparent;color:#fff}.file.is-success:focus .file-cta,.file.is-success.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(35,209,96,0.25);color:#fff}.file.is-success:active .file-cta,.file.is-success.is-active .file-cta{background-color:#20bc56;border-color:transparent;color:#fff}.file.is-warning .file-cta{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,0.7)}.file.is-warning:hover .file-cta,.file.is-warning.is-hovered .file-cta{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,0.7)}.file.is-warning:focus .file-cta,.file.is-warning.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(255,221,87,0.25);color:rgba(0,0,0,0.7)}.file.is-warning:active .file-cta,.file.is-warning.is-active .file-cta{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,0.7)}.file.is-danger .file-cta{background-color:#ff3860;border-color:transparent;color:#fff}.file.is-danger:hover .file-cta,.file.is-danger.is-hovered .file-cta{background-color:#ff2b56;border-color:transparent;color:#fff}.file.is-danger:focus .file-cta,.file.is-danger.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(255,56,96,0.25);color:#fff}.file.is-danger:active .file-cta,.file.is-danger.is-active .file-cta{background-color:#ff1f4b;border-color:transparent;color:#fff}.file.is-small{font-size:.75rem}.file.is-medium{font-size:1.25rem}.file.is-medium .file-icon .fa{font-size:21px}.file.is-large{font-size:1.5rem}.file.is-large .file-icon .fa{font-size:28px}.file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}.file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}.file.has-name.is-empty .file-cta{border-radius:4px}.file.has-name.is-empty .file-name{display:none}.file.is-boxed .file-label{flex-direction:column}.file.is-boxed .file-cta{flex-direction:column;height:auto;padding:1em 3em}.file.is-boxed .file-name{border-width:0 1px 1px}.file.is-boxed .file-icon{height:1.5em;width:1.5em}.file.is-boxed .file-icon .fa{font-size:21px}.file.is-boxed.is-small .file-icon .fa{font-size:14px}.file.is-boxed.is-medium .file-icon .fa{font-size:28px}.file.is-boxed.is-large .file-icon .fa{font-size:35px}.file.is-boxed.has-name .file-cta{border-radius:4px 4px 0 0}.file.is-boxed.has-name .file-name{border-radius:0 0 4px 4px;border-width:0 1px 1px}.file.is-centered{justify-content:center}.file.is-fullwidth .file-label{width:100%}.file.is-fullwidth .file-name{flex-grow:1;max-width:none}.file.is-right{justify-content:flex-end}.file.is-right .file-cta{border-radius:0 4px 4px 0}.file.is-right .file-name{border-radius:4px 0 0 4px;border-width:1px 0 1px 1px;order:-1}.file-label{align-items:stretch;display:flex;cursor:pointer;justify-content:flex-start;overflow:hidden;position:relative}.file-label:hover .file-cta{background-color:#eee;color:#363636}.file-label:hover .file-name{border-color:#d5d5d5}.file-label:active .file-cta{background-color:#e8e8e8;color:#363636}.file-label:active .file-name{border-color:#cfcfcf}.file-input{height:100%;left:0;opacity:0;outline:none;position:absolute;top:0;width:100%}.file-cta,.file-name{border-color:#dbdbdb;border-radius:4px;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}.file-cta{background-color:#f5f5f5;color:#4a4a4a}.file-name{border-color:#dbdbdb;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:left;text-overflow:ellipsis}.file-icon{align-items:center;display:flex;height:1em;justify-content:center;margin-right:0.5em;width:1em}.file-icon .fa{font-size:14px}.label{color:#363636;display:block;font-size:1rem;font-weight:700}.label:not(:last-child){margin-bottom:0.5em}.label.is-small{font-size:.75rem}.label.is-medium{font-size:1.25rem}.label.is-large{font-size:1.5rem}.help{display:block;font-size:.75rem;margin-top:0.25rem}.help.is-white{color:#fff}.help.is-black{color:#0a0a0a}.help.is-light{color:#f5f5f5}.help.is-dark{color:#363636}.help.is-primary{color:#00d1b2}.help.is-link{color:#3273dc}.help.is-info{color:#209cee}.help.is-success{color:#23d160}.help.is-warning{color:#ffdd57}.help.is-danger{color:#ff3860}.field:not(:last-child){margin-bottom:0.75rem}.field.has-addons{display:flex;justify-content:flex-start}.field.has-addons .control:not(:last-child){margin-right:-1px}.field.has-addons .control:not(:first-child):not(:last-child) .button,.field.has-addons .control:not(:first-child):not(:last-child) .input,.field.has-addons .control:not(:first-child):not(:last-child) .select select{border-radius:0}.field.has-addons .control:first-child:not(:only-child) .button,.field.has-addons .control:first-child:not(:only-child) .input,.field.has-addons .control:first-child:not(:only-child) .select select{border-bottom-right-radius:0;border-top-right-radius:0}.field.has-addons .control:last-child:not(:only-child) .button,.field.has-addons .control:last-child:not(:only-child) .input,.field.has-addons .control:last-child:not(:only-child) .select select{border-bottom-left-radius:0;border-top-left-radius:0}.field.has-addons .control .button:not([disabled]):hover,.field.has-addons .control .button:not([disabled]).is-hovered,.field.has-addons .control .input:not([disabled]):hover,.field.has-addons .control .input:not([disabled]).is-hovered,.field.has-addons .control .select select:not([disabled]):hover,.field.has-addons .control .select select:not([disabled]).is-hovered{z-index:2}.field.has-addons .control .button:not([disabled]):focus,.field.has-addons .control .button:not([disabled]).is-focused,.field.has-addons .control .button:not([disabled]):active,.field.has-addons .control .button:not([disabled]).is-active,.field.has-addons .control .input:not([disabled]):focus,.field.has-addons .control .input:not([disabled]).is-focused,.field.has-addons .control .input:not([disabled]):active,.field.has-addons .control .input:not([disabled]).is-active,.field.has-addons .control .select select:not([disabled]):focus,.field.has-addons .control .select select:not([disabled]).is-focused,.field.has-addons .control .select select:not([disabled]):active,.field.has-addons .control .select select:not([disabled]).is-active{z-index:3}.field.has-addons .control .button:not([disabled]):focus:hover,.field.has-addons .control .button:not([disabled]).is-focused:hover,.field.has-addons .control .button:not([disabled]):active:hover,.field.has-addons .control .button:not([disabled]).is-active:hover,.field.has-addons .control .input:not([disabled]):focus:hover,.field.has-addons .control .input:not([disabled]).is-focused:hover,.field.has-addons .control .input:not([disabled]):active:hover,.field.has-addons .control .input:not([disabled]).is-active:hover,.field.has-addons .control .select select:not([disabled]):focus:hover,.field.has-addons .control .select select:not([disabled]).is-focused:hover,.field.has-addons .control .select select:not([disabled]):active:hover,.field.has-addons .control .select select:not([disabled]).is-active:hover{z-index:4}.field.has-addons .control.is-expanded{flex-grow:1;flex-shrink:1}.field.has-addons.has-addons-centered{justify-content:center}.field.has-addons.has-addons-right{justify-content:flex-end}.field.has-addons.has-addons-fullwidth .control{flex-grow:1;flex-shrink:0}.field.is-grouped{display:flex;justify-content:flex-start}.field.is-grouped>.control{flex-shrink:0}.field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:0.75rem}.field.is-grouped>.control.is-expanded{flex-grow:1;flex-shrink:1}.field.is-grouped.is-grouped-centered{justify-content:center}.field.is-grouped.is-grouped-right{justify-content:flex-end}.field.is-grouped.is-grouped-multiline{flex-wrap:wrap}.field.is-grouped.is-grouped-multiline>.control:last-child,.field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:0.75rem}.field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-0.75rem}.field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media screen and (min-width: 769px), print{.field.is-horizontal{display:flex}}.field-label .label{font-size:inherit}@media screen and (max-width: 768px){.field-label{margin-bottom:0.5rem}}@media screen and (min-width: 769px), print{.field-label{flex-basis:0;flex-grow:1;flex-shrink:0;margin-right:1.5rem;text-align:right}.field-label.is-small{font-size:.75rem;padding-top:0.375em}.field-label.is-normal{padding-top:0.375em}.field-label.is-medium{font-size:1.25rem;padding-top:0.375em}.field-label.is-large{font-size:1.5rem;padding-top:0.375em}}.field-body .field .field{margin-bottom:0}@media screen and (min-width: 769px), print{.field-body{display:flex;flex-basis:0;flex-grow:5;flex-shrink:1}.field-body .field{margin-bottom:0}.field-body>.field{flex-shrink:1}.field-body>.field:not(.is-narrow){flex-grow:1}.field-body>.field:not(:last-child){margin-right:0.75rem}}.control{box-sizing:border-box;clear:both;font-size:1rem;position:relative;text-align:left}.control.has-icons-left .input:focus ~ .icon,.control.has-icons-left .select:focus ~ .icon,.control.has-icons-right .input:focus ~ .icon,.control.has-icons-right .select:focus ~ .icon{color:#7a7a7a}.control.has-icons-left .input.is-small ~ .icon,.control.has-icons-left .select.is-small ~ .icon,.control.has-icons-right .input.is-small ~ .icon,.control.has-icons-right .select.is-small ~ .icon{font-size:.75rem}.control.has-icons-left .input.is-medium ~ .icon,.control.has-icons-left .select.is-medium ~ .icon,.control.has-icons-right .input.is-medium ~ .icon,.control.has-icons-right .select.is-medium ~ .icon{font-size:1.25rem}.control.has-icons-left .input.is-large ~ .icon,.control.has-icons-left .select.is-large ~ .icon,.control.has-icons-right .input.is-large ~ .icon,.control.has-icons-right .select.is-large ~ .icon{font-size:1.5rem}.control.has-icons-left .icon,.control.has-icons-right .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icons-left .input,.control.has-icons-left .select select{padding-left:2.25em}.control.has-icons-left .icon.is-left{left:0}.control.has-icons-right .input,.control.has-icons-right .select select{padding-right:2.25em}.control.has-icons-right .icon.is-right{right:0}.control.is-loading::after{position:absolute !important;right:0.625em;top:0.625em;z-index:4}.control.is-loading.is-small:after{font-size:.75rem}.control.is-loading.is-medium:after{font-size:1.25rem}.control.is-loading.is-large:after{font-size:1.5rem}.breadcrumb{font-size:1rem;white-space:nowrap}.breadcrumb a{align-items:center;color:#3273dc;display:flex;justify-content:center;padding:0 .75em}.breadcrumb a:hover{color:#363636}.breadcrumb li{align-items:center;display:flex}.breadcrumb li:first-child a{padding-left:0}.breadcrumb li.is-active a{color:#363636;cursor:default;pointer-events:none}.breadcrumb li+li::before{color:#b5b5b5;content:"\0002f"}.breadcrumb ul,.breadcrumb ol{align-items:flex-start;display:flex;flex-wrap:wrap;justify-content:flex-start}.breadcrumb .icon:first-child{margin-right:0.5em}.breadcrumb .icon:last-child{margin-left:0.5em}.breadcrumb.is-centered ol,.breadcrumb.is-centered ul{justify-content:center}.breadcrumb.is-right ol,.breadcrumb.is-right ul{justify-content:flex-end}.breadcrumb.is-small{font-size:.75rem}.breadcrumb.is-medium{font-size:1.25rem}.breadcrumb.is-large{font-size:1.5rem}.breadcrumb.has-arrow-separator li+li::before{content:"\02192"}.breadcrumb.has-bullet-separator li+li::before{content:"\02022"}.breadcrumb.has-dot-separator li+li::before{content:"\000b7"}.breadcrumb.has-succeeds-separator li+li::before{content:"\0227B"}.card{background-color:#fff;box-shadow:0 2px 3px rgba(10,10,10,0.1),0 0 0 1px rgba(10,10,10,0.1);color:#4a4a4a;max-width:100%;position:relative}.card-header{background-color:rgba(0,0,0,0);align-items:stretch;box-shadow:0 1px 2px rgba(10,10,10,0.1);display:flex}.card-header-title{align-items:center;color:#363636;display:flex;flex-grow:1;font-weight:700;padding:.75rem}.card-header-title.is-centered{justify-content:center}.card-header-icon{align-items:center;cursor:pointer;display:flex;justify-content:center;padding:.75rem}.card-image{display:block;position:relative}.card-content{background-color:rgba(0,0,0,0);padding:1.5rem}.card-footer{background-color:rgba(0,0,0,0);border-top:1px solid #dbdbdb;align-items:stretch;display:flex}.card-footer-item{align-items:center;display:flex;flex-basis:0;flex-grow:1;flex-shrink:0;justify-content:center;padding:.75rem}.card-footer-item:not(:last-child){border-right:1px solid #dbdbdb}.card .media:not(:last-child){margin-bottom:1.5rem}.dropdown{display:inline-flex;position:relative;vertical-align:top}.dropdown.is-active .dropdown-menu,.dropdown.is-hoverable:hover .dropdown-menu{display:block}.dropdown.is-right .dropdown-menu{left:auto;right:0}.dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:initial;top:auto}.dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}.dropdown-content{background-color:#fff;border-radius:4px;box-shadow:0 2px 3px rgba(10,10,10,0.1),0 0 0 1px rgba(10,10,10,0.1);padding-bottom:.5rem;padding-top:.5rem}.dropdown-item{color:#4a4a4a;display:block;font-size:0.875rem;line-height:1.5;padding:0.375rem 1rem;position:relative}a.dropdown-item,button.dropdown-item{padding-right:3rem;text-align:left;white-space:nowrap;width:100%}a.dropdown-item:hover,button.dropdown-item:hover{background-color:#f5f5f5;color:#0a0a0a}a.dropdown-item.is-active,button.dropdown-item.is-active{background-color:#3273dc;color:#fff}.dropdown-divider{background-color:#dbdbdb;border:none;display:block;height:1px;margin:0.5rem 0}.level{align-items:center;justify-content:space-between}.level code{border-radius:4px}.level img{display:inline-block;vertical-align:top}.level.is-mobile{display:flex}.level.is-mobile .level-left,.level.is-mobile .level-right{display:flex}.level.is-mobile .level-left+.level-right{margin-top:0}.level.is-mobile .level-item:not(:last-child){margin-bottom:0;margin-right:.75rem}.level.is-mobile .level-item:not(.is-narrow){flex-grow:1}@media screen and (min-width: 769px), print{.level{display:flex}.level>.level-item:not(.is-narrow){flex-grow:1}}.level-item{align-items:center;display:flex;flex-basis:auto;flex-grow:0;flex-shrink:0;justify-content:center}.level-item .title,.level-item .subtitle{margin-bottom:0}@media screen and (max-width: 768px){.level-item:not(:last-child){margin-bottom:.75rem}}.level-left,.level-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.level-left .level-item.is-flexible,.level-right .level-item.is-flexible{flex-grow:1}@media screen and (min-width: 769px), print{.level-left .level-item:not(:last-child),.level-right .level-item:not(:last-child){margin-right:.75rem}}.level-left{align-items:center;justify-content:flex-start}@media screen and (max-width: 768px){.level-left+.level-right{margin-top:1.5rem}}@media screen and (min-width: 769px), print{.level-left{display:flex}}.level-right{align-items:center;justify-content:flex-end}@media screen and (min-width: 769px), print{.level-right{display:flex}}.list{background-color:#fff;border-radius:4px;box-shadow:0 2px 3px rgba(10,10,10,0.1),0 0 0 1px rgba(10,10,10,0.1)}.list-item{display:block;padding:0.5em 1em}.list-item:not(a){color:#4a4a4a}.list-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-item:last-child{border-bottom-left-radius:4px;border-bottom-right-radius:4px}.list-item:not(:last-child){border-bottom:1px solid #dbdbdb}.list-item.is-active{background-color:#3273dc;color:#fff}a.list-item{background-color:#f5f5f5;cursor:pointer}.media{align-items:flex-start;display:flex;text-align:left}.media .content:not(:last-child){margin-bottom:0.75rem}.media .media{border-top:1px solid rgba(219,219,219,0.5);display:flex;padding-top:0.75rem}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:0.5rem}.media .media .media{padding-top:0.5rem}.media .media .media+.media{margin-top:0.5rem}.media+.media{border-top:1px solid rgba(219,219,219,0.5);margin-top:1rem;padding-top:1rem}.media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}.media-left,.media-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.media-left{margin-right:1rem}.media-right{margin-left:1rem}.media-content{flex-basis:auto;flex-grow:1;flex-shrink:1;text-align:left}@media screen and (max-width: 768px){.media-content{overflow-x:auto}}.menu{font-size:1rem}.menu.is-small{font-size:.75rem}.menu.is-medium{font-size:1.25rem}.menu.is-large{font-size:1.5rem}.menu-list{line-height:1.25}.menu-list a{border-radius:2px;color:#4a4a4a;display:block;padding:0.5em 0.75em}.menu-list a:hover{background-color:#f5f5f5;color:#363636}.menu-list a.is-active{background-color:#3273dc;color:#fff}.menu-list li ul{border-left:1px solid #dbdbdb;margin:.75em;padding-left:.75em}.menu-label{color:#7a7a7a;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}.menu-label:not(:first-child){margin-top:1em}.menu-label:not(:last-child){margin-bottom:1em}.message{background-color:#f5f5f5;border-radius:4px;font-size:1rem}.message strong{color:currentColor}.message a:not(.button):not(.tag):not(.dropdown-item){color:currentColor;text-decoration:underline}.message.is-small{font-size:.75rem}.message.is-medium{font-size:1.25rem}.message.is-large{font-size:1.5rem}.message.is-white{background-color:#fff}.message.is-white .message-header{background-color:#fff;color:#0a0a0a}.message.is-white .message-body{border-color:#fff;color:#4d4d4d}.message.is-black{background-color:#fafafa}.message.is-black .message-header{background-color:#0a0a0a;color:#fff}.message.is-black .message-body{border-color:#0a0a0a;color:#090909}.message.is-light{background-color:#fafafa}.message.is-light .message-header{background-color:#f5f5f5;color:#363636}.message.is-light .message-body{border-color:#f5f5f5;color:#505050}.message.is-dark{background-color:#fafafa}.message.is-dark .message-header{background-color:#363636;color:#f5f5f5}.message.is-dark .message-body{border-color:#363636;color:#2a2a2a}.message.is-primary{background-color:#f5fffd}.message.is-primary .message-header{background-color:#00d1b2;color:#fff}.message.is-primary .message-body{border-color:#00d1b2;color:#021310}.message.is-link{background-color:#f6f9fe}.message.is-link .message-header{background-color:#3273dc;color:#fff}.message.is-link .message-body{border-color:#3273dc;color:#22509a}.message.is-info{background-color:#f6fbfe}.message.is-info .message-header{background-color:#209cee;color:#fff}.message.is-info .message-body{border-color:#209cee;color:#12537e}.message.is-success{background-color:#f6fef9}.message.is-success .message-header{background-color:#23d160;color:#fff}.message.is-success .message-body{border-color:#23d160;color:#0e301a}.message.is-warning{background-color:#fffdf5}.message.is-warning .message-header{background-color:#ffdd57;color:rgba(0,0,0,0.7)}.message.is-warning .message-body{border-color:#ffdd57;color:#3b3108}.message.is-danger{background-color:#fff5f7}.message.is-danger .message-header{background-color:#ff3860;color:#fff}.message.is-danger .message-body{border-color:#ff3860;color:#cd0930}.message-header{align-items:center;background-color:#4a4a4a;border-radius:4px 4px 0 0;color:#fff;display:flex;font-weight:700;justify-content:space-between;line-height:1.25;padding:0.75em 1em;position:relative}.message-header .delete{flex-grow:0;flex-shrink:0;margin-left:0.75em}.message-header+.message-body{border-width:0;border-top-left-radius:0;border-top-right-radius:0}.message-body{border-color:#dbdbdb;border-radius:4px;border-style:solid;border-width:0 0 0 4px;color:#4a4a4a;padding:1.25em 1.5em}.message-body code,.message-body pre{background-color:#fff}.message-body pre code{background-color:rgba(0,0,0,0)}.modal{align-items:center;display:none;flex-direction:column;justify-content:center;overflow:hidden;position:fixed;z-index:40}.modal.is-active{display:flex}.modal-background{background-color:rgba(10,10,10,0.86)}.modal-content,.modal-card{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media screen and (min-width: 769px), print{.modal-content,.modal-card{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{background:none;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal-card{display:flex;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden;-ms-overflow-y:visible}.modal-card-head,.modal-card-foot{align-items:center;background-color:#f5f5f5;display:flex;flex-shrink:0;justify-content:flex-start;padding:20px;position:relative}.modal-card-head{border-bottom:1px solid #dbdbdb;border-top-left-radius:6px;border-top-right-radius:6px}.modal-card-title{color:#363636;flex-grow:1;flex-shrink:0;font-size:1.5rem;line-height:1}.modal-card-foot{border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:1px solid #dbdbdb}.modal-card-foot .button:not(:last-child){margin-right:0.5em}.modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;flex-grow:1;flex-shrink:1;overflow:auto;padding:20px}.navbar{background-color:#fff;min-height:3.25rem;position:relative;z-index:30}.navbar.is-white{background-color:#fff;color:#0a0a0a}.navbar.is-white .navbar-brand>.navbar-item,.navbar.is-white .navbar-brand .navbar-link{color:#0a0a0a}.navbar.is-white .navbar-brand>a.navbar-item:focus,.navbar.is-white .navbar-brand>a.navbar-item:hover,.navbar.is-white .navbar-brand>a.navbar-item.is-active,.navbar.is-white .navbar-brand .navbar-link:focus,.navbar.is-white .navbar-brand .navbar-link:hover,.navbar.is-white .navbar-brand .navbar-link.is-active{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-burger{color:#0a0a0a}@media screen and (min-width: 1024px){.navbar.is-white .navbar-start>.navbar-item,.navbar.is-white .navbar-start .navbar-link,.navbar.is-white .navbar-end>.navbar-item,.navbar.is-white .navbar-end .navbar-link{color:#0a0a0a}.navbar.is-white .navbar-start>a.navbar-item:focus,.navbar.is-white .navbar-start>a.navbar-item:hover,.navbar.is-white .navbar-start>a.navbar-item.is-active,.navbar.is-white .navbar-start .navbar-link:focus,.navbar.is-white .navbar-start .navbar-link:hover,.navbar.is-white .navbar-start .navbar-link.is-active,.navbar.is-white .navbar-end>a.navbar-item:focus,.navbar.is-white .navbar-end>a.navbar-item:hover,.navbar.is-white .navbar-end>a.navbar-item.is-active,.navbar.is-white .navbar-end .navbar-link:focus,.navbar.is-white .navbar-end .navbar-link:hover,.navbar.is-white .navbar-end .navbar-link.is-active{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-start .navbar-link::after,.navbar.is-white .navbar-end .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-white .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}.navbar.is-black{background-color:#0a0a0a;color:#fff}.navbar.is-black .navbar-brand>.navbar-item,.navbar.is-black .navbar-brand .navbar-link{color:#fff}.navbar.is-black .navbar-brand>a.navbar-item:focus,.navbar.is-black .navbar-brand>a.navbar-item:hover,.navbar.is-black .navbar-brand>a.navbar-item.is-active,.navbar.is-black .navbar-brand .navbar-link:focus,.navbar.is-black .navbar-brand .navbar-link:hover,.navbar.is-black .navbar-brand .navbar-link.is-active{background-color:#000;color:#fff}.navbar.is-black .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-burger{color:#fff}@media screen and (min-width: 1024px){.navbar.is-black .navbar-start>.navbar-item,.navbar.is-black .navbar-start .navbar-link,.navbar.is-black .navbar-end>.navbar-item,.navbar.is-black .navbar-end .navbar-link{color:#fff}.navbar.is-black .navbar-start>a.navbar-item:focus,.navbar.is-black .navbar-start>a.navbar-item:hover,.navbar.is-black .navbar-start>a.navbar-item.is-active,.navbar.is-black .navbar-start .navbar-link:focus,.navbar.is-black .navbar-start .navbar-link:hover,.navbar.is-black .navbar-start .navbar-link.is-active,.navbar.is-black .navbar-end>a.navbar-item:focus,.navbar.is-black .navbar-end>a.navbar-item:hover,.navbar.is-black .navbar-end>a.navbar-item.is-active,.navbar.is-black .navbar-end .navbar-link:focus,.navbar.is-black .navbar-end .navbar-link:hover,.navbar.is-black .navbar-end .navbar-link.is-active{background-color:#000;color:#fff}.navbar.is-black .navbar-start .navbar-link::after,.navbar.is-black .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-black .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link{background-color:#000;color:#fff}.navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}.navbar.is-light{background-color:#f5f5f5;color:#363636}.navbar.is-light .navbar-brand>.navbar-item,.navbar.is-light .navbar-brand .navbar-link{color:#363636}.navbar.is-light .navbar-brand>a.navbar-item:focus,.navbar.is-light .navbar-brand>a.navbar-item:hover,.navbar.is-light .navbar-brand>a.navbar-item.is-active,.navbar.is-light .navbar-brand .navbar-link:focus,.navbar.is-light .navbar-brand .navbar-link:hover,.navbar.is-light .navbar-brand .navbar-link.is-active{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-brand .navbar-link::after{border-color:#363636}.navbar.is-light .navbar-burger{color:#363636}@media screen and (min-width: 1024px){.navbar.is-light .navbar-start>.navbar-item,.navbar.is-light .navbar-start .navbar-link,.navbar.is-light .navbar-end>.navbar-item,.navbar.is-light .navbar-end .navbar-link{color:#363636}.navbar.is-light .navbar-start>a.navbar-item:focus,.navbar.is-light .navbar-start>a.navbar-item:hover,.navbar.is-light .navbar-start>a.navbar-item.is-active,.navbar.is-light .navbar-start .navbar-link:focus,.navbar.is-light .navbar-start .navbar-link:hover,.navbar.is-light .navbar-start .navbar-link.is-active,.navbar.is-light .navbar-end>a.navbar-item:focus,.navbar.is-light .navbar-end>a.navbar-item:hover,.navbar.is-light .navbar-end>a.navbar-item.is-active,.navbar.is-light .navbar-end .navbar-link:focus,.navbar.is-light .navbar-end .navbar-link:hover,.navbar.is-light .navbar-end .navbar-link.is-active{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-start .navbar-link::after,.navbar.is-light .navbar-end .navbar-link::after{border-color:#363636}.navbar.is-light .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-light .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#363636}}.navbar.is-dark{background-color:#363636;color:#f5f5f5}.navbar.is-dark .navbar-brand>.navbar-item,.navbar.is-dark .navbar-brand .navbar-link{color:#f5f5f5}.navbar.is-dark .navbar-brand>a.navbar-item:focus,.navbar.is-dark .navbar-brand>a.navbar-item:hover,.navbar.is-dark .navbar-brand>a.navbar-item.is-active,.navbar.is-dark .navbar-brand .navbar-link:focus,.navbar.is-dark .navbar-brand .navbar-link:hover,.navbar.is-dark .navbar-brand .navbar-link.is-active{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link::after{border-color:#f5f5f5}.navbar.is-dark .navbar-burger{color:#f5f5f5}@media screen and (min-width: 1024px){.navbar.is-dark .navbar-start>.navbar-item,.navbar.is-dark .navbar-start .navbar-link,.navbar.is-dark .navbar-end>.navbar-item,.navbar.is-dark .navbar-end .navbar-link{color:#f5f5f5}.navbar.is-dark .navbar-start>a.navbar-item:focus,.navbar.is-dark .navbar-start>a.navbar-item:hover,.navbar.is-dark .navbar-start>a.navbar-item.is-active,.navbar.is-dark .navbar-start .navbar-link:focus,.navbar.is-dark .navbar-start .navbar-link:hover,.navbar.is-dark .navbar-start .navbar-link.is-active,.navbar.is-dark .navbar-end>a.navbar-item:focus,.navbar.is-dark .navbar-end>a.navbar-item:hover,.navbar.is-dark .navbar-end>a.navbar-item.is-active,.navbar.is-dark .navbar-end .navbar-link:focus,.navbar.is-dark .navbar-end .navbar-link:hover,.navbar.is-dark .navbar-end .navbar-link.is-active{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-start .navbar-link::after,.navbar.is-dark .navbar-end .navbar-link::after{border-color:#f5f5f5}.navbar.is-dark .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-dropdown a.navbar-item.is-active{background-color:#363636;color:#f5f5f5}}.navbar.is-primary{background-color:#00d1b2;color:#fff}.navbar.is-primary .navbar-brand>.navbar-item,.navbar.is-primary .navbar-brand .navbar-link{color:#fff}.navbar.is-primary .navbar-brand>a.navbar-item:focus,.navbar.is-primary .navbar-brand>a.navbar-item:hover,.navbar.is-primary .navbar-brand>a.navbar-item.is-active,.navbar.is-primary .navbar-brand .navbar-link:focus,.navbar.is-primary .navbar-brand .navbar-link:hover,.navbar.is-primary .navbar-brand .navbar-link.is-active{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-burger{color:#fff}@media screen and (min-width: 1024px){.navbar.is-primary .navbar-start>.navbar-item,.navbar.is-primary .navbar-start .navbar-link,.navbar.is-primary .navbar-end>.navbar-item,.navbar.is-primary .navbar-end .navbar-link{color:#fff}.navbar.is-primary .navbar-start>a.navbar-item:focus,.navbar.is-primary .navbar-start>a.navbar-item:hover,.navbar.is-primary .navbar-start>a.navbar-item.is-active,.navbar.is-primary .navbar-start .navbar-link:focus,.navbar.is-primary .navbar-start .navbar-link:hover,.navbar.is-primary .navbar-start .navbar-link.is-active,.navbar.is-primary .navbar-end>a.navbar-item:focus,.navbar.is-primary .navbar-end>a.navbar-item:hover,.navbar.is-primary .navbar-end>a.navbar-item.is-active,.navbar.is-primary .navbar-end .navbar-link:focus,.navbar.is-primary .navbar-end .navbar-link:hover,.navbar.is-primary .navbar-end .navbar-link.is-active{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-start .navbar-link::after,.navbar.is-primary .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-dropdown a.navbar-item.is-active{background-color:#00d1b2;color:#fff}}.navbar.is-link{background-color:#3273dc;color:#fff}.navbar.is-link .navbar-brand>.navbar-item,.navbar.is-link .navbar-brand .navbar-link{color:#fff}.navbar.is-link .navbar-brand>a.navbar-item:focus,.navbar.is-link .navbar-brand>a.navbar-item:hover,.navbar.is-link .navbar-brand>a.navbar-item.is-active,.navbar.is-link .navbar-brand .navbar-link:focus,.navbar.is-link .navbar-brand .navbar-link:hover,.navbar.is-link .navbar-brand .navbar-link.is-active{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-burger{color:#fff}@media screen and (min-width: 1024px){.navbar.is-link .navbar-start>.navbar-item,.navbar.is-link .navbar-start .navbar-link,.navbar.is-link .navbar-end>.navbar-item,.navbar.is-link .navbar-end .navbar-link{color:#fff}.navbar.is-link .navbar-start>a.navbar-item:focus,.navbar.is-link .navbar-start>a.navbar-item:hover,.navbar.is-link .navbar-start>a.navbar-item.is-active,.navbar.is-link .navbar-start .navbar-link:focus,.navbar.is-link .navbar-start .navbar-link:hover,.navbar.is-link .navbar-start .navbar-link.is-active,.navbar.is-link .navbar-end>a.navbar-item:focus,.navbar.is-link .navbar-end>a.navbar-item:hover,.navbar.is-link .navbar-end>a.navbar-item.is-active,.navbar.is-link .navbar-end .navbar-link:focus,.navbar.is-link .navbar-end .navbar-link:hover,.navbar.is-link .navbar-end .navbar-link.is-active{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-start .navbar-link::after,.navbar.is-link .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-link .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#3273dc;color:#fff}}.navbar.is-info{background-color:#209cee;color:#fff}.navbar.is-info .navbar-brand>.navbar-item,.navbar.is-info .navbar-brand .navbar-link{color:#fff}.navbar.is-info .navbar-brand>a.navbar-item:focus,.navbar.is-info .navbar-brand>a.navbar-item:hover,.navbar.is-info .navbar-brand>a.navbar-item.is-active,.navbar.is-info .navbar-brand .navbar-link:focus,.navbar.is-info .navbar-brand .navbar-link:hover,.navbar.is-info .navbar-brand .navbar-link.is-active{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-burger{color:#fff}@media screen and (min-width: 1024px){.navbar.is-info .navbar-start>.navbar-item,.navbar.is-info .navbar-start .navbar-link,.navbar.is-info .navbar-end>.navbar-item,.navbar.is-info .navbar-end .navbar-link{color:#fff}.navbar.is-info .navbar-start>a.navbar-item:focus,.navbar.is-info .navbar-start>a.navbar-item:hover,.navbar.is-info .navbar-start>a.navbar-item.is-active,.navbar.is-info .navbar-start .navbar-link:focus,.navbar.is-info .navbar-start .navbar-link:hover,.navbar.is-info .navbar-start .navbar-link.is-active,.navbar.is-info .navbar-end>a.navbar-item:focus,.navbar.is-info .navbar-end>a.navbar-item:hover,.navbar.is-info .navbar-end>a.navbar-item.is-active,.navbar.is-info .navbar-end .navbar-link:focus,.navbar.is-info .navbar-end .navbar-link:hover,.navbar.is-info .navbar-end .navbar-link.is-active{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-start .navbar-link::after,.navbar.is-info .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-info .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#209cee;color:#fff}}.navbar.is-success{background-color:#23d160;color:#fff}.navbar.is-success .navbar-brand>.navbar-item,.navbar.is-success .navbar-brand .navbar-link{color:#fff}.navbar.is-success .navbar-brand>a.navbar-item:focus,.navbar.is-success .navbar-brand>a.navbar-item:hover,.navbar.is-success .navbar-brand>a.navbar-item.is-active,.navbar.is-success .navbar-brand .navbar-link:focus,.navbar.is-success .navbar-brand .navbar-link:hover,.navbar.is-success .navbar-brand .navbar-link.is-active{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-burger{color:#fff}@media screen and (min-width: 1024px){.navbar.is-success .navbar-start>.navbar-item,.navbar.is-success .navbar-start .navbar-link,.navbar.is-success .navbar-end>.navbar-item,.navbar.is-success .navbar-end .navbar-link{color:#fff}.navbar.is-success .navbar-start>a.navbar-item:focus,.navbar.is-success .navbar-start>a.navbar-item:hover,.navbar.is-success .navbar-start>a.navbar-item.is-active,.navbar.is-success .navbar-start .navbar-link:focus,.navbar.is-success .navbar-start .navbar-link:hover,.navbar.is-success .navbar-start .navbar-link.is-active,.navbar.is-success .navbar-end>a.navbar-item:focus,.navbar.is-success .navbar-end>a.navbar-item:hover,.navbar.is-success .navbar-end>a.navbar-item.is-active,.navbar.is-success .navbar-end .navbar-link:focus,.navbar.is-success .navbar-end .navbar-link:hover,.navbar.is-success .navbar-end .navbar-link.is-active{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-start .navbar-link::after,.navbar.is-success .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-success .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#23d160;color:#fff}}.navbar.is-warning{background-color:#ffdd57;color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-brand>.navbar-item,.navbar.is-warning .navbar-brand .navbar-link{color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-brand>a.navbar-item:focus,.navbar.is-warning .navbar-brand>a.navbar-item:hover,.navbar.is-warning .navbar-brand>a.navbar-item.is-active,.navbar.is-warning .navbar-brand .navbar-link:focus,.navbar.is-warning .navbar-brand .navbar-link:hover,.navbar.is-warning .navbar-brand .navbar-link.is-active{background-color:#ffd83d;color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-burger{color:rgba(0,0,0,0.7)}@media screen and (min-width: 1024px){.navbar.is-warning .navbar-start>.navbar-item,.navbar.is-warning .navbar-start .navbar-link,.navbar.is-warning .navbar-end>.navbar-item,.navbar.is-warning .navbar-end .navbar-link{color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-start>a.navbar-item:focus,.navbar.is-warning .navbar-start>a.navbar-item:hover,.navbar.is-warning .navbar-start>a.navbar-item.is-active,.navbar.is-warning .navbar-start .navbar-link:focus,.navbar.is-warning .navbar-start .navbar-link:hover,.navbar.is-warning .navbar-start .navbar-link.is-active,.navbar.is-warning .navbar-end>a.navbar-item:focus,.navbar.is-warning .navbar-end>a.navbar-item:hover,.navbar.is-warning .navbar-end>a.navbar-item.is-active,.navbar.is-warning .navbar-end .navbar-link:focus,.navbar.is-warning .navbar-end .navbar-link:hover,.navbar.is-warning .navbar-end .navbar-link.is-active{background-color:#ffd83d;color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-start .navbar-link::after,.navbar.is-warning .navbar-end .navbar-link::after{border-color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link{background-color:#ffd83d;color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ffdd57;color:rgba(0,0,0,0.7)}}.navbar.is-danger{background-color:#ff3860;color:#fff}.navbar.is-danger .navbar-brand>.navbar-item,.navbar.is-danger .navbar-brand .navbar-link{color:#fff}.navbar.is-danger .navbar-brand>a.navbar-item:focus,.navbar.is-danger .navbar-brand>a.navbar-item:hover,.navbar.is-danger .navbar-brand>a.navbar-item.is-active,.navbar.is-danger .navbar-brand .navbar-link:focus,.navbar.is-danger .navbar-brand .navbar-link:hover,.navbar.is-danger .navbar-brand .navbar-link.is-active{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-burger{color:#fff}@media screen and (min-width: 1024px){.navbar.is-danger .navbar-start>.navbar-item,.navbar.is-danger .navbar-start .navbar-link,.navbar.is-danger .navbar-end>.navbar-item,.navbar.is-danger .navbar-end .navbar-link{color:#fff}.navbar.is-danger .navbar-start>a.navbar-item:focus,.navbar.is-danger .navbar-start>a.navbar-item:hover,.navbar.is-danger .navbar-start>a.navbar-item.is-active,.navbar.is-danger .navbar-start .navbar-link:focus,.navbar.is-danger .navbar-start .navbar-link:hover,.navbar.is-danger .navbar-start .navbar-link.is-active,.navbar.is-danger .navbar-end>a.navbar-item:focus,.navbar.is-danger .navbar-end>a.navbar-item:hover,.navbar.is-danger .navbar-end>a.navbar-item.is-active,.navbar.is-danger .navbar-end .navbar-link:focus,.navbar.is-danger .navbar-end .navbar-link:hover,.navbar.is-danger .navbar-end .navbar-link.is-active{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-start .navbar-link::after,.navbar.is-danger .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#ff3860;color:#fff}}.navbar>.container{align-items:stretch;display:flex;min-height:3.25rem;width:100%}.navbar.has-shadow{box-shadow:0 2px 0 0 #f5f5f5}.navbar.is-fixed-bottom,.navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom{bottom:0}.navbar.is-fixed-bottom.has-shadow{box-shadow:0 -2px 0 0 #f5f5f5}.navbar.is-fixed-top{top:0}html.has-navbar-fixed-top,body.has-navbar-fixed-top{padding-top:3.25rem}html.has-navbar-fixed-bottom,body.has-navbar-fixed-bottom{padding-bottom:3.25rem}.navbar-brand,.navbar-tabs{align-items:stretch;display:flex;flex-shrink:0;min-height:3.25rem}.navbar-brand a.navbar-item:focus,.navbar-brand a.navbar-item:hover{background-color:transparent}.navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}.navbar-burger{color:#4a4a4a;cursor:pointer;display:block;height:3.25rem;position:relative;width:3.25rem;margin-left:auto}.navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;-webkit-transform-origin:center;transform-origin:center;transition-duration:86ms;transition-property:background-color, opacity, -webkit-transform;transition-property:background-color, opacity, transform;transition-property:background-color, opacity, transform, -webkit-transform;transition-timing-function:ease-out;width:16px}.navbar-burger span:nth-child(1){top:calc(50% - 6px)}.navbar-burger span:nth-child(2){top:calc(50% - 1px)}.navbar-burger span:nth-child(3){top:calc(50% + 4px)}.navbar-burger:hover{background-color:rgba(0,0,0,0.05)}.navbar-burger.is-active span:nth-child(1){-webkit-transform:translateY(5px) rotate(45deg);transform:translateY(5px) rotate(45deg)}.navbar-burger.is-active span:nth-child(2){opacity:0}.navbar-burger.is-active span:nth-child(3){-webkit-transform:translateY(-5px) rotate(-45deg);transform:translateY(-5px) rotate(-45deg)}.navbar-menu{display:none}.navbar-item,.navbar-link{color:#4a4a4a;display:block;line-height:1.5;padding:0.5rem 0.75rem;position:relative}.navbar-item .icon:only-child,.navbar-link .icon:only-child{margin-left:-0.25rem;margin-right:-0.25rem}a.navbar-item,.navbar-link{cursor:pointer}a.navbar-item:focus,a.navbar-item:focus-within,a.navbar-item:hover,a.navbar-item.is-active,.navbar-link:focus,.navbar-link:focus-within,.navbar-link:hover,.navbar-link.is-active{background-color:#fafafa;color:#3273dc}.navbar-item{display:block;flex-grow:0;flex-shrink:0}.navbar-item img{max-height:1.75rem}.navbar-item.has-dropdown{padding:0}.navbar-item.is-expanded{flex-grow:1;flex-shrink:1}.navbar-item.is-tab{border-bottom:1px solid transparent;min-height:3.25rem;padding-bottom:calc(0.5rem - 1px)}.navbar-item.is-tab:focus,.navbar-item.is-tab:hover{background-color:rgba(0,0,0,0);border-bottom-color:#3273dc}.navbar-item.is-tab.is-active{background-color:rgba(0,0,0,0);border-bottom-color:#3273dc;border-bottom-style:solid;border-bottom-width:3px;color:#3273dc;padding-bottom:calc(0.5rem - 3px)}.navbar-content{flex-grow:1;flex-shrink:1}.navbar-link:not(.is-arrowless){padding-right:2.5em}.navbar-link:not(.is-arrowless)::after{border-color:#3273dc;margin-top:-0.375em;right:1.125em}.navbar-dropdown{font-size:0.875rem;padding-bottom:0.5rem;padding-top:0.5rem}.navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}.navbar-divider{background-color:#f5f5f5;border:none;display:none;height:2px;margin:0.5rem 0}@media screen and (max-width: 1023px){.navbar>.container{display:block}.navbar-brand .navbar-item,.navbar-tabs .navbar-item{align-items:center;display:flex}.navbar-link::after{display:none}.navbar-menu{background-color:#fff;box-shadow:0 8px 16px rgba(10,10,10,0.1);padding:0.5rem 0}.navbar-menu.is-active{display:block}.navbar.is-fixed-bottom-touch,.navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-touch{bottom:0}.navbar.is-fixed-bottom-touch.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,0.1)}.navbar.is-fixed-top-touch{top:0}.navbar.is-fixed-top .navbar-menu,.navbar.is-fixed-top-touch .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 3.25rem);overflow:auto}html.has-navbar-fixed-top-touch,body.has-navbar-fixed-top-touch{padding-top:3.25rem}html.has-navbar-fixed-bottom-touch,body.has-navbar-fixed-bottom-touch{padding-bottom:3.25rem}}@media screen and (min-width: 1024px){.navbar,.navbar-menu,.navbar-start,.navbar-end{align-items:stretch;display:flex}.navbar{min-height:3.25rem}.navbar.is-spaced{padding:1rem 2rem}.navbar.is-spaced .navbar-start,.navbar.is-spaced .navbar-end{align-items:center}.navbar.is-spaced a.navbar-item,.navbar.is-spaced .navbar-link{border-radius:4px}.navbar.is-transparent a.navbar-item:focus,.navbar.is-transparent a.navbar-item:hover,.navbar.is-transparent a.navbar-item.is-active,.navbar.is-transparent .navbar-link:focus,.navbar.is-transparent .navbar-link:hover,.navbar.is-transparent .navbar-link.is-active{background-color:transparent !important}.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus-within .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link{background-color:transparent !important}.navbar.is-transparent .navbar-dropdown a.navbar-item:focus,.navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-burger{display:none}.navbar-item,.navbar-link{align-items:center;display:flex}.navbar-item{display:flex}.navbar-item.has-dropdown{align-items:stretch}.navbar-item.has-dropdown-up .navbar-link::after{-webkit-transform:rotate(135deg) translate(0.25em, -0.25em);transform:rotate(135deg) translate(0.25em, -0.25em)}.navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:2px solid #dbdbdb;border-radius:6px 6px 0 0;border-top:none;bottom:100%;box-shadow:0 -8px 8px rgba(10,10,10,0.1);top:auto}.navbar-item.is-active .navbar-dropdown,.navbar-item.is-hoverable:focus .navbar-dropdown,.navbar-item.is-hoverable:focus-within .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar.is-spaced .navbar-item.is-active .navbar-dropdown,.navbar-item.is-active .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-hoverable:focus .navbar-dropdown,.navbar-item.is-hoverable:focus .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-hoverable:focus-within .navbar-dropdown,.navbar-item.is-hoverable:focus-within .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed{opacity:1;pointer-events:auto;-webkit-transform:translateY(0);transform:translateY(0)}.navbar-menu{flex-grow:1;flex-shrink:0}.navbar-start{justify-content:flex-start;margin-right:auto}.navbar-end{justify-content:flex-end;margin-left:auto}.navbar-dropdown{background-color:#fff;border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:2px solid #dbdbdb;box-shadow:0 8px 8px rgba(10,10,10,0.1);display:none;font-size:0.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}.navbar-dropdown .navbar-item{padding:0.375rem 1rem;white-space:nowrap}.navbar-dropdown a.navbar-item{padding-right:3rem}.navbar-dropdown a.navbar-item:focus,.navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar.is-spaced .navbar-dropdown,.navbar-dropdown.is-boxed{border-radius:6px;border-top:none;box-shadow:0 8px 8px rgba(10,10,10,0.1),0 0 0 1px rgba(10,10,10,0.1);display:block;opacity:0;pointer-events:none;top:calc(100% + (-4px));-webkit-transform:translateY(-5px);transform:translateY(-5px);transition-duration:86ms;transition-property:opacity, -webkit-transform;transition-property:opacity, transform;transition-property:opacity, transform, -webkit-transform}.navbar-dropdown.is-right{left:auto;right:0}.navbar-divider{display:block}.navbar>.container .navbar-brand,.container>.navbar .navbar-brand{margin-left:-.75rem}.navbar>.container .navbar-menu,.container>.navbar .navbar-menu{margin-right:-.75rem}.navbar.is-fixed-bottom-desktop,.navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-desktop{bottom:0}.navbar.is-fixed-bottom-desktop.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,0.1)}.navbar.is-fixed-top-desktop{top:0}html.has-navbar-fixed-top-desktop,body.has-navbar-fixed-top-desktop{padding-top:3.25rem}html.has-navbar-fixed-bottom-desktop,body.has-navbar-fixed-bottom-desktop{padding-bottom:3.25rem}html.has-spaced-navbar-fixed-top,body.has-spaced-navbar-fixed-top{padding-top:5.25rem}html.has-spaced-navbar-fixed-bottom,body.has-spaced-navbar-fixed-bottom{padding-bottom:5.25rem}a.navbar-item.is-active,.navbar-link.is-active{color:#0a0a0a}a.navbar-item.is-active:not(:focus):not(:hover),.navbar-link.is-active:not(:focus):not(:hover){background-color:rgba(0,0,0,0)}.navbar-item.has-dropdown:focus .navbar-link,.navbar-item.has-dropdown:hover .navbar-link,.navbar-item.has-dropdown.is-active .navbar-link{background-color:#fafafa}}.hero.is-fullheight-with-navbar{min-height:calc(100vh - 3.25rem)}.pagination{font-size:1rem;margin:-.25rem}.pagination.is-small{font-size:.75rem}.pagination.is-medium{font-size:1.25rem}.pagination.is-large{font-size:1.5rem}.pagination.is-rounded .pagination-previous,.pagination.is-rounded .pagination-next{padding-left:1em;padding-right:1em;border-radius:290486px}.pagination.is-rounded .pagination-link{border-radius:290486px}.pagination,.pagination-list{align-items:center;display:flex;justify-content:center;text-align:center}.pagination-previous,.pagination-next,.pagination-link,.pagination-ellipsis{font-size:1em;justify-content:center;margin:.25rem;padding-left:.5em;padding-right:.5em;text-align:center}.pagination-previous,.pagination-next,.pagination-link{border-color:#dbdbdb;color:#363636;min-width:2.25em}.pagination-previous:hover,.pagination-next:hover,.pagination-link:hover{border-color:#b5b5b5;color:#363636}.pagination-previous:focus,.pagination-next:focus,.pagination-link:focus{border-color:#3273dc}.pagination-previous:active,.pagination-next:active,.pagination-link:active{box-shadow:inset 0 1px 2px rgba(10,10,10,0.2)}.pagination-previous[disabled],.pagination-next[disabled],.pagination-link[disabled]{background-color:#dbdbdb;border-color:#dbdbdb;box-shadow:none;color:#7a7a7a;opacity:0.5}.pagination-previous,.pagination-next{padding-left:0.75em;padding-right:0.75em;white-space:nowrap}.pagination-link.is-current{background-color:#3273dc;border-color:#3273dc;color:#fff}.pagination-ellipsis{color:#b5b5b5;pointer-events:none}.pagination-list{flex-wrap:wrap}@media screen and (max-width: 768px){.pagination{flex-wrap:wrap}.pagination-previous,.pagination-next{flex-grow:1;flex-shrink:1}.pagination-list li{flex-grow:1;flex-shrink:1}}@media screen and (min-width: 769px), print{.pagination-list{flex-grow:1;flex-shrink:1;justify-content:flex-start;order:1}.pagination-previous{order:2}.pagination-next{order:3}.pagination{justify-content:space-between}.pagination.is-centered .pagination-previous{order:1}.pagination.is-centered .pagination-list{justify-content:center;order:2}.pagination.is-centered .pagination-next{order:3}.pagination.is-right .pagination-previous{order:1}.pagination.is-right .pagination-next{order:2}.pagination.is-right .pagination-list{justify-content:flex-end;order:3}}.panel{font-size:1rem}.panel:not(:last-child){margin-bottom:1.5rem}.panel-heading,.panel-tabs,.panel-block{border-bottom:1px solid #dbdbdb;border-left:1px solid #dbdbdb;border-right:1px solid #dbdbdb}.panel-heading:first-child,.panel-tabs:first-child,.panel-block:first-child{border-top:1px solid #dbdbdb}.panel-heading{background-color:#f5f5f5;border-radius:4px 4px 0 0;color:#363636;font-size:1.25em;font-weight:300;line-height:1.25;padding:0.5em 0.75em}.panel-tabs{align-items:flex-end;display:flex;font-size:.875em;justify-content:center}.panel-tabs a{border-bottom:1px solid #dbdbdb;margin-bottom:-1px;padding:0.5em}.panel-tabs a.is-active{border-bottom-color:#4a4a4a;color:#363636}.panel-list a{color:#4a4a4a}.panel-list a:hover{color:#3273dc}.panel-block{align-items:center;color:#363636;display:flex;justify-content:flex-start;padding:0.5em 0.75em}.panel-block input[type="checkbox"]{margin-right:0.75em}.panel-block>.control{flex-grow:1;flex-shrink:1;width:100%}.panel-block.is-wrapped{flex-wrap:wrap}.panel-block.is-active{border-left-color:#3273dc;color:#363636}.panel-block.is-active .panel-icon{color:#3273dc}a.panel-block,label.panel-block{cursor:pointer}a.panel-block:hover,label.panel-block:hover{background-color:#f5f5f5}.panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#7a7a7a;margin-right:0.75em}.panel-icon .fa{font-size:inherit;line-height:inherit}.tabs{-webkit-overflow-scrolling:touch;align-items:stretch;display:flex;font-size:1rem;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs a{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;color:#4a4a4a;display:flex;justify-content:center;margin-bottom:-1px;padding:0.5em 1em;vertical-align:top}.tabs a:hover{border-bottom-color:#363636;color:#363636}.tabs li{display:block}.tabs li.is-active a{border-bottom-color:#3273dc;color:#3273dc}.tabs ul{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;display:flex;flex-grow:1;flex-shrink:0;justify-content:flex-start}.tabs ul.is-left{padding-right:0.75em}.tabs ul.is-center{flex:none;justify-content:center;padding-left:0.75em;padding-right:0.75em}.tabs ul.is-right{justify-content:flex-end;padding-left:0.75em}.tabs .icon:first-child{margin-right:0.5em}.tabs .icon:last-child{margin-left:0.5em}.tabs.is-centered ul{justify-content:center}.tabs.is-right ul{justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:4px 4px 0 0}.tabs.is-boxed a:hover{background-color:#f5f5f5;border-bottom-color:#dbdbdb}.tabs.is-boxed li.is-active a{background-color:#fff;border-color:#dbdbdb;border-bottom-color:rgba(0,0,0,0) !important}.tabs.is-fullwidth li{flex-grow:1;flex-shrink:0}.tabs.is-toggle a{border-color:#dbdbdb;border-style:solid;border-width:1px;margin-bottom:0;position:relative}.tabs.is-toggle a:hover{background-color:#f5f5f5;border-color:#b5b5b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-radius:4px 0 0 4px}.tabs.is-toggle li:last-child a{border-radius:0 4px 4px 0}.tabs.is-toggle li.is-active a{background-color:#3273dc;border-color:#3273dc;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}.tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:290486px;border-top-left-radius:290486px;padding-left:1.25em}.tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:290486px;border-top-right-radius:290486px;padding-right:1.25em}.tabs.is-small{font-size:.75rem}.tabs.is-medium{font-size:1.25rem}.tabs.is-large{font-size:1.5rem}.column{display:block;flex-basis:0;flex-grow:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>.column.is-narrow{flex:none}.columns.is-mobile>.column.is-full{flex:none;width:100%}.columns.is-mobile>.column.is-three-quarters{flex:none;width:75%}.columns.is-mobile>.column.is-two-thirds{flex:none;width:66.6666%}.columns.is-mobile>.column.is-half{flex:none;width:50%}.columns.is-mobile>.column.is-one-third{flex:none;width:33.3333%}.columns.is-mobile>.column.is-one-quarter{flex:none;width:25%}.columns.is-mobile>.column.is-one-fifth{flex:none;width:20%}.columns.is-mobile>.column.is-two-fifths{flex:none;width:40%}.columns.is-mobile>.column.is-three-fifths{flex:none;width:60%}.columns.is-mobile>.column.is-four-fifths{flex:none;width:80%}.columns.is-mobile>.column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>.column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>.column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>.column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>.column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>.column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>.column.is-0{flex:none;width:0%}.columns.is-mobile>.column.is-offset-0{margin-left:0%}.columns.is-mobile>.column.is-1{flex:none;width:8.33333%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333%}.columns.is-mobile>.column.is-2{flex:none;width:16.66667%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66667%}.columns.is-mobile>.column.is-3{flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{flex:none;width:33.33333%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333%}.columns.is-mobile>.column.is-5{flex:none;width:41.66667%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66667%}.columns.is-mobile>.column.is-6{flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{flex:none;width:58.33333%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333%}.columns.is-mobile>.column.is-8{flex:none;width:66.66667%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66667%}.columns.is-mobile>.column.is-9{flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{flex:none;width:83.33333%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333%}.columns.is-mobile>.column.is-11{flex:none;width:91.66667%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66667%}.columns.is-mobile>.column.is-12{flex:none;width:100%}.columns.is-mobile>.column.is-offset-12{margin-left:100%}@media screen and (max-width: 768px){.column.is-narrow-mobile{flex:none}.column.is-full-mobile{flex:none;width:100%}.column.is-three-quarters-mobile{flex:none;width:75%}.column.is-two-thirds-mobile{flex:none;width:66.6666%}.column.is-half-mobile{flex:none;width:50%}.column.is-one-third-mobile{flex:none;width:33.3333%}.column.is-one-quarter-mobile{flex:none;width:25%}.column.is-one-fifth-mobile{flex:none;width:20%}.column.is-two-fifths-mobile{flex:none;width:40%}.column.is-three-fifths-mobile{flex:none;width:60%}.column.is-four-fifths-mobile{flex:none;width:80%}.column.is-offset-three-quarters-mobile{margin-left:75%}.column.is-offset-two-thirds-mobile{margin-left:66.6666%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-one-third-mobile{margin-left:33.3333%}.column.is-offset-one-quarter-mobile{margin-left:25%}.column.is-offset-one-fifth-mobile{margin-left:20%}.column.is-offset-two-fifths-mobile{margin-left:40%}.column.is-offset-three-fifths-mobile{margin-left:60%}.column.is-offset-four-fifths-mobile{margin-left:80%}.column.is-0-mobile{flex:none;width:0%}.column.is-offset-0-mobile{margin-left:0%}.column.is-1-mobile{flex:none;width:8.33333%}.column.is-offset-1-mobile{margin-left:8.33333%}.column.is-2-mobile{flex:none;width:16.66667%}.column.is-offset-2-mobile{margin-left:16.66667%}.column.is-3-mobile{flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{flex:none;width:33.33333%}.column.is-offset-4-mobile{margin-left:33.33333%}.column.is-5-mobile{flex:none;width:41.66667%}.column.is-offset-5-mobile{margin-left:41.66667%}.column.is-6-mobile{flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{flex:none;width:58.33333%}.column.is-offset-7-mobile{margin-left:58.33333%}.column.is-8-mobile{flex:none;width:66.66667%}.column.is-offset-8-mobile{margin-left:66.66667%}.column.is-9-mobile{flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{flex:none;width:83.33333%}.column.is-offset-10-mobile{margin-left:83.33333%}.column.is-11-mobile{flex:none;width:91.66667%}.column.is-offset-11-mobile{margin-left:91.66667%}.column.is-12-mobile{flex:none;width:100%}.column.is-offset-12-mobile{margin-left:100%}}@media screen and (min-width: 769px), print{.column.is-narrow,.column.is-narrow-tablet{flex:none}.column.is-full,.column.is-full-tablet{flex:none;width:100%}.column.is-three-quarters,.column.is-three-quarters-tablet{flex:none;width:75%}.column.is-two-thirds,.column.is-two-thirds-tablet{flex:none;width:66.6666%}.column.is-half,.column.is-half-tablet{flex:none;width:50%}.column.is-one-third,.column.is-one-third-tablet{flex:none;width:33.3333%}.column.is-one-quarter,.column.is-one-quarter-tablet{flex:none;width:25%}.column.is-one-fifth,.column.is-one-fifth-tablet{flex:none;width:20%}.column.is-two-fifths,.column.is-two-fifths-tablet{flex:none;width:40%}.column.is-three-fifths,.column.is-three-fifths-tablet{flex:none;width:60%}.column.is-four-fifths,.column.is-four-fifths-tablet{flex:none;width:80%}.column.is-offset-three-quarters,.column.is-offset-three-quarters-tablet{margin-left:75%}.column.is-offset-two-thirds,.column.is-offset-two-thirds-tablet{margin-left:66.6666%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-one-third,.column.is-offset-one-third-tablet{margin-left:33.3333%}.column.is-offset-one-quarter,.column.is-offset-one-quarter-tablet{margin-left:25%}.column.is-offset-one-fifth,.column.is-offset-one-fifth-tablet{margin-left:20%}.column.is-offset-two-fifths,.column.is-offset-two-fifths-tablet{margin-left:40%}.column.is-offset-three-fifths,.column.is-offset-three-fifths-tablet{margin-left:60%}.column.is-offset-four-fifths,.column.is-offset-four-fifths-tablet{margin-left:80%}.column.is-0,.column.is-0-tablet{flex:none;width:0%}.column.is-offset-0,.column.is-offset-0-tablet{margin-left:0%}.column.is-1,.column.is-1-tablet{flex:none;width:8.33333%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333%}.column.is-2,.column.is-2-tablet{flex:none;width:16.66667%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66667%}.column.is-3,.column.is-3-tablet{flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{flex:none;width:33.33333%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333%}.column.is-5,.column.is-5-tablet{flex:none;width:41.66667%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66667%}.column.is-6,.column.is-6-tablet{flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{flex:none;width:58.33333%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333%}.column.is-8,.column.is-8-tablet{flex:none;width:66.66667%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66667%}.column.is-9,.column.is-9-tablet{flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{flex:none;width:83.33333%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333%}.column.is-11,.column.is-11-tablet{flex:none;width:91.66667%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66667%}.column.is-12,.column.is-12-tablet{flex:none;width:100%}.column.is-offset-12,.column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width: 1023px){.column.is-narrow-touch{flex:none}.column.is-full-touch{flex:none;width:100%}.column.is-three-quarters-touch{flex:none;width:75%}.column.is-two-thirds-touch{flex:none;width:66.6666%}.column.is-half-touch{flex:none;width:50%}.column.is-one-third-touch{flex:none;width:33.3333%}.column.is-one-quarter-touch{flex:none;width:25%}.column.is-one-fifth-touch{flex:none;width:20%}.column.is-two-fifths-touch{flex:none;width:40%}.column.is-three-fifths-touch{flex:none;width:60%}.column.is-four-fifths-touch{flex:none;width:80%}.column.is-offset-three-quarters-touch{margin-left:75%}.column.is-offset-two-thirds-touch{margin-left:66.6666%}.column.is-offset-half-touch{margin-left:50%}.column.is-offset-one-third-touch{margin-left:33.3333%}.column.is-offset-one-quarter-touch{margin-left:25%}.column.is-offset-one-fifth-touch{margin-left:20%}.column.is-offset-two-fifths-touch{margin-left:40%}.column.is-offset-three-fifths-touch{margin-left:60%}.column.is-offset-four-fifths-touch{margin-left:80%}.column.is-0-touch{flex:none;width:0%}.column.is-offset-0-touch{margin-left:0%}.column.is-1-touch{flex:none;width:8.33333%}.column.is-offset-1-touch{margin-left:8.33333%}.column.is-2-touch{flex:none;width:16.66667%}.column.is-offset-2-touch{margin-left:16.66667%}.column.is-3-touch{flex:none;width:25%}.column.is-offset-3-touch{margin-left:25%}.column.is-4-touch{flex:none;width:33.33333%}.column.is-offset-4-touch{margin-left:33.33333%}.column.is-5-touch{flex:none;width:41.66667%}.column.is-offset-5-touch{margin-left:41.66667%}.column.is-6-touch{flex:none;width:50%}.column.is-offset-6-touch{margin-left:50%}.column.is-7-touch{flex:none;width:58.33333%}.column.is-offset-7-touch{margin-left:58.33333%}.column.is-8-touch{flex:none;width:66.66667%}.column.is-offset-8-touch{margin-left:66.66667%}.column.is-9-touch{flex:none;width:75%}.column.is-offset-9-touch{margin-left:75%}.column.is-10-touch{flex:none;width:83.33333%}.column.is-offset-10-touch{margin-left:83.33333%}.column.is-11-touch{flex:none;width:91.66667%}.column.is-offset-11-touch{margin-left:91.66667%}.column.is-12-touch{flex:none;width:100%}.column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width: 1024px){.column.is-narrow-desktop{flex:none}.column.is-full-desktop{flex:none;width:100%}.column.is-three-quarters-desktop{flex:none;width:75%}.column.is-two-thirds-desktop{flex:none;width:66.6666%}.column.is-half-desktop{flex:none;width:50%}.column.is-one-third-desktop{flex:none;width:33.3333%}.column.is-one-quarter-desktop{flex:none;width:25%}.column.is-one-fifth-desktop{flex:none;width:20%}.column.is-two-fifths-desktop{flex:none;width:40%}.column.is-three-fifths-desktop{flex:none;width:60%}.column.is-four-fifths-desktop{flex:none;width:80%}.column.is-offset-three-quarters-desktop{margin-left:75%}.column.is-offset-two-thirds-desktop{margin-left:66.6666%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-one-third-desktop{margin-left:33.3333%}.column.is-offset-one-quarter-desktop{margin-left:25%}.column.is-offset-one-fifth-desktop{margin-left:20%}.column.is-offset-two-fifths-desktop{margin-left:40%}.column.is-offset-three-fifths-desktop{margin-left:60%}.column.is-offset-four-fifths-desktop{margin-left:80%}.column.is-0-desktop{flex:none;width:0%}.column.is-offset-0-desktop{margin-left:0%}.column.is-1-desktop{flex:none;width:8.33333%}.column.is-offset-1-desktop{margin-left:8.33333%}.column.is-2-desktop{flex:none;width:16.66667%}.column.is-offset-2-desktop{margin-left:16.66667%}.column.is-3-desktop{flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{flex:none;width:33.33333%}.column.is-offset-4-desktop{margin-left:33.33333%}.column.is-5-desktop{flex:none;width:41.66667%}.column.is-offset-5-desktop{margin-left:41.66667%}.column.is-6-desktop{flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{flex:none;width:58.33333%}.column.is-offset-7-desktop{margin-left:58.33333%}.column.is-8-desktop{flex:none;width:66.66667%}.column.is-offset-8-desktop{margin-left:66.66667%}.column.is-9-desktop{flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{flex:none;width:83.33333%}.column.is-offset-10-desktop{margin-left:83.33333%}.column.is-11-desktop{flex:none;width:91.66667%}.column.is-offset-11-desktop{margin-left:91.66667%}.column.is-12-desktop{flex:none;width:100%}.column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width: 1216px){.column.is-narrow-widescreen{flex:none}.column.is-full-widescreen{flex:none;width:100%}.column.is-three-quarters-widescreen{flex:none;width:75%}.column.is-two-thirds-widescreen{flex:none;width:66.6666%}.column.is-half-widescreen{flex:none;width:50%}.column.is-one-third-widescreen{flex:none;width:33.3333%}.column.is-one-quarter-widescreen{flex:none;width:25%}.column.is-one-fifth-widescreen{flex:none;width:20%}.column.is-two-fifths-widescreen{flex:none;width:40%}.column.is-three-fifths-widescreen{flex:none;width:60%}.column.is-four-fifths-widescreen{flex:none;width:80%}.column.is-offset-three-quarters-widescreen{margin-left:75%}.column.is-offset-two-thirds-widescreen{margin-left:66.6666%}.column.is-offset-half-widescreen{margin-left:50%}.column.is-offset-one-third-widescreen{margin-left:33.3333%}.column.is-offset-one-quarter-widescreen{margin-left:25%}.column.is-offset-one-fifth-widescreen{margin-left:20%}.column.is-offset-two-fifths-widescreen{margin-left:40%}.column.is-offset-three-fifths-widescreen{margin-left:60%}.column.is-offset-four-fifths-widescreen{margin-left:80%}.column.is-0-widescreen{flex:none;width:0%}.column.is-offset-0-widescreen{margin-left:0%}.column.is-1-widescreen{flex:none;width:8.33333%}.column.is-offset-1-widescreen{margin-left:8.33333%}.column.is-2-widescreen{flex:none;width:16.66667%}.column.is-offset-2-widescreen{margin-left:16.66667%}.column.is-3-widescreen{flex:none;width:25%}.column.is-offset-3-widescreen{margin-left:25%}.column.is-4-widescreen{flex:none;width:33.33333%}.column.is-offset-4-widescreen{margin-left:33.33333%}.column.is-5-widescreen{flex:none;width:41.66667%}.column.is-offset-5-widescreen{margin-left:41.66667%}.column.is-6-widescreen{flex:none;width:50%}.column.is-offset-6-widescreen{margin-left:50%}.column.is-7-widescreen{flex:none;width:58.33333%}.column.is-offset-7-widescreen{margin-left:58.33333%}.column.is-8-widescreen{flex:none;width:66.66667%}.column.is-offset-8-widescreen{margin-left:66.66667%}.column.is-9-widescreen{flex:none;width:75%}.column.is-offset-9-widescreen{margin-left:75%}.column.is-10-widescreen{flex:none;width:83.33333%}.column.is-offset-10-widescreen{margin-left:83.33333%}.column.is-11-widescreen{flex:none;width:91.66667%}.column.is-offset-11-widescreen{margin-left:91.66667%}.column.is-12-widescreen{flex:none;width:100%}.column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width: 1408px){.column.is-narrow-fullhd{flex:none}.column.is-full-fullhd{flex:none;width:100%}.column.is-three-quarters-fullhd{flex:none;width:75%}.column.is-two-thirds-fullhd{flex:none;width:66.6666%}.column.is-half-fullhd{flex:none;width:50%}.column.is-one-third-fullhd{flex:none;width:33.3333%}.column.is-one-quarter-fullhd{flex:none;width:25%}.column.is-one-fifth-fullhd{flex:none;width:20%}.column.is-two-fifths-fullhd{flex:none;width:40%}.column.is-three-fifths-fullhd{flex:none;width:60%}.column.is-four-fifths-fullhd{flex:none;width:80%}.column.is-offset-three-quarters-fullhd{margin-left:75%}.column.is-offset-two-thirds-fullhd{margin-left:66.6666%}.column.is-offset-half-fullhd{margin-left:50%}.column.is-offset-one-third-fullhd{margin-left:33.3333%}.column.is-offset-one-quarter-fullhd{margin-left:25%}.column.is-offset-one-fifth-fullhd{margin-left:20%}.column.is-offset-two-fifths-fullhd{margin-left:40%}.column.is-offset-three-fifths-fullhd{margin-left:60%}.column.is-offset-four-fifths-fullhd{margin-left:80%}.column.is-0-fullhd{flex:none;width:0%}.column.is-offset-0-fullhd{margin-left:0%}.column.is-1-fullhd{flex:none;width:8.33333%}.column.is-offset-1-fullhd{margin-left:8.33333%}.column.is-2-fullhd{flex:none;width:16.66667%}.column.is-offset-2-fullhd{margin-left:16.66667%}.column.is-3-fullhd{flex:none;width:25%}.column.is-offset-3-fullhd{margin-left:25%}.column.is-4-fullhd{flex:none;width:33.33333%}.column.is-offset-4-fullhd{margin-left:33.33333%}.column.is-5-fullhd{flex:none;width:41.66667%}.column.is-offset-5-fullhd{margin-left:41.66667%}.column.is-6-fullhd{flex:none;width:50%}.column.is-offset-6-fullhd{margin-left:50%}.column.is-7-fullhd{flex:none;width:58.33333%}.column.is-offset-7-fullhd{margin-left:58.33333%}.column.is-8-fullhd{flex:none;width:66.66667%}.column.is-offset-8-fullhd{margin-left:66.66667%}.column.is-9-fullhd{flex:none;width:75%}.column.is-offset-9-fullhd{margin-left:75%}.column.is-10-fullhd{flex:none;width:83.33333%}.column.is-offset-10-fullhd{margin-left:83.33333%}.column.is-11-fullhd{flex:none;width:91.66667%}.column.is-offset-11-fullhd{margin-left:91.66667%}.column.is-12-fullhd{flex:none;width:100%}.column.is-offset-12-fullhd{margin-left:100%}}.columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.columns:last-child{margin-bottom:-.75rem}.columns:not(:last-child){margin-bottom:calc(1.5rem - .75rem)}.columns.is-centered{justify-content:center}.columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}.columns.is-gapless>.column{margin:0;padding:0 !important}.columns.is-gapless:not(:last-child){margin-bottom:1.5rem}.columns.is-gapless:last-child{margin-bottom:0}.columns.is-mobile{display:flex}.columns.is-multiline{flex-wrap:wrap}.columns.is-vcentered{align-items:center}@media screen and (min-width: 769px), print{.columns:not(.is-desktop){display:flex}}@media screen and (min-width: 1024px){.columns.is-desktop{display:flex}}.columns.is-variable{--columnGap: 0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}.columns.is-variable .column{padding-left:var(--columnGap);padding-right:var(--columnGap)}.columns.is-variable.is-0{--columnGap: 0rem}@media screen and (max-width: 768px){.columns.is-variable.is-0-mobile{--columnGap: 0rem}}@media screen and (min-width: 769px), print{.columns.is-variable.is-0-tablet{--columnGap: 0rem}}@media screen and (min-width: 769px) and (max-width: 1023px){.columns.is-variable.is-0-tablet-only{--columnGap: 0rem}}@media screen and (max-width: 1023px){.columns.is-variable.is-0-touch{--columnGap: 0rem}}@media screen and (min-width: 1024px){.columns.is-variable.is-0-desktop{--columnGap: 0rem}}@media screen and (min-width: 1024px) and (max-width: 1215px){.columns.is-variable.is-0-desktop-only{--columnGap: 0rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-0-widescreen{--columnGap: 0rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-0-widescreen-only{--columnGap: 0rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-0-fullhd{--columnGap: 0rem}}.columns.is-variable.is-1{--columnGap: .25rem}@media screen and (max-width: 768px){.columns.is-variable.is-1-mobile{--columnGap: .25rem}}@media screen and (min-width: 769px), print{.columns.is-variable.is-1-tablet{--columnGap: .25rem}}@media screen and (min-width: 769px) and (max-width: 1023px){.columns.is-variable.is-1-tablet-only{--columnGap: .25rem}}@media screen and (max-width: 1023px){.columns.is-variable.is-1-touch{--columnGap: .25rem}}@media screen and (min-width: 1024px){.columns.is-variable.is-1-desktop{--columnGap: .25rem}}@media screen and (min-width: 1024px) and (max-width: 1215px){.columns.is-variable.is-1-desktop-only{--columnGap: .25rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-1-widescreen{--columnGap: .25rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-1-widescreen-only{--columnGap: .25rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-1-fullhd{--columnGap: .25rem}}.columns.is-variable.is-2{--columnGap: .5rem}@media screen and (max-width: 768px){.columns.is-variable.is-2-mobile{--columnGap: .5rem}}@media screen and (min-width: 769px), print{.columns.is-variable.is-2-tablet{--columnGap: .5rem}}@media screen and (min-width: 769px) and (max-width: 1023px){.columns.is-variable.is-2-tablet-only{--columnGap: .5rem}}@media screen and (max-width: 1023px){.columns.is-variable.is-2-touch{--columnGap: .5rem}}@media screen and (min-width: 1024px){.columns.is-variable.is-2-desktop{--columnGap: .5rem}}@media screen and (min-width: 1024px) and (max-width: 1215px){.columns.is-variable.is-2-desktop-only{--columnGap: .5rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-2-widescreen{--columnGap: .5rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-2-widescreen-only{--columnGap: .5rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-2-fullhd{--columnGap: .5rem}}.columns.is-variable.is-3{--columnGap: .75rem}@media screen and (max-width: 768px){.columns.is-variable.is-3-mobile{--columnGap: .75rem}}@media screen and (min-width: 769px), print{.columns.is-variable.is-3-tablet{--columnGap: .75rem}}@media screen and (min-width: 769px) and (max-width: 1023px){.columns.is-variable.is-3-tablet-only{--columnGap: .75rem}}@media screen and (max-width: 1023px){.columns.is-variable.is-3-touch{--columnGap: .75rem}}@media screen and (min-width: 1024px){.columns.is-variable.is-3-desktop{--columnGap: .75rem}}@media screen and (min-width: 1024px) and (max-width: 1215px){.columns.is-variable.is-3-desktop-only{--columnGap: .75rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-3-widescreen{--columnGap: .75rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-3-widescreen-only{--columnGap: .75rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-3-fullhd{--columnGap: .75rem}}.columns.is-variable.is-4{--columnGap: 1rem}@media screen and (max-width: 768px){.columns.is-variable.is-4-mobile{--columnGap: 1rem}}@media screen and (min-width: 769px), print{.columns.is-variable.is-4-tablet{--columnGap: 1rem}}@media screen and (min-width: 769px) and (max-width: 1023px){.columns.is-variable.is-4-tablet-only{--columnGap: 1rem}}@media screen and (max-width: 1023px){.columns.is-variable.is-4-touch{--columnGap: 1rem}}@media screen and (min-width: 1024px){.columns.is-variable.is-4-desktop{--columnGap: 1rem}}@media screen and (min-width: 1024px) and (max-width: 1215px){.columns.is-variable.is-4-desktop-only{--columnGap: 1rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-4-widescreen{--columnGap: 1rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-4-widescreen-only{--columnGap: 1rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-4-fullhd{--columnGap: 1rem}}.columns.is-variable.is-5{--columnGap: 1.25rem}@media screen and (max-width: 768px){.columns.is-variable.is-5-mobile{--columnGap: 1.25rem}}@media screen and (min-width: 769px), print{.columns.is-variable.is-5-tablet{--columnGap: 1.25rem}}@media screen and (min-width: 769px) and (max-width: 1023px){.columns.is-variable.is-5-tablet-only{--columnGap: 1.25rem}}@media screen and (max-width: 1023px){.columns.is-variable.is-5-touch{--columnGap: 1.25rem}}@media screen and (min-width: 1024px){.columns.is-variable.is-5-desktop{--columnGap: 1.25rem}}@media screen and (min-width: 1024px) and (max-width: 1215px){.columns.is-variable.is-5-desktop-only{--columnGap: 1.25rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-5-widescreen{--columnGap: 1.25rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-5-widescreen-only{--columnGap: 1.25rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-5-fullhd{--columnGap: 1.25rem}}.columns.is-variable.is-6{--columnGap: 1.5rem}@media screen and (max-width: 768px){.columns.is-variable.is-6-mobile{--columnGap: 1.5rem}}@media screen and (min-width: 769px), print{.columns.is-variable.is-6-tablet{--columnGap: 1.5rem}}@media screen and (min-width: 769px) and (max-width: 1023px){.columns.is-variable.is-6-tablet-only{--columnGap: 1.5rem}}@media screen and (max-width: 1023px){.columns.is-variable.is-6-touch{--columnGap: 1.5rem}}@media screen and (min-width: 1024px){.columns.is-variable.is-6-desktop{--columnGap: 1.5rem}}@media screen and (min-width: 1024px) and (max-width: 1215px){.columns.is-variable.is-6-desktop-only{--columnGap: 1.5rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-6-widescreen{--columnGap: 1.5rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-6-widescreen-only{--columnGap: 1.5rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-6-fullhd{--columnGap: 1.5rem}}.columns.is-variable.is-7{--columnGap: 1.75rem}@media screen and (max-width: 768px){.columns.is-variable.is-7-mobile{--columnGap: 1.75rem}}@media screen and (min-width: 769px), print{.columns.is-variable.is-7-tablet{--columnGap: 1.75rem}}@media screen and (min-width: 769px) and (max-width: 1023px){.columns.is-variable.is-7-tablet-only{--columnGap: 1.75rem}}@media screen and (max-width: 1023px){.columns.is-variable.is-7-touch{--columnGap: 1.75rem}}@media screen and (min-width: 1024px){.columns.is-variable.is-7-desktop{--columnGap: 1.75rem}}@media screen and (min-width: 1024px) and (max-width: 1215px){.columns.is-variable.is-7-desktop-only{--columnGap: 1.75rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-7-widescreen{--columnGap: 1.75rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-7-widescreen-only{--columnGap: 1.75rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-7-fullhd{--columnGap: 1.75rem}}.columns.is-variable.is-8{--columnGap: 2rem}@media screen and (max-width: 768px){.columns.is-variable.is-8-mobile{--columnGap: 2rem}}@media screen and (min-width: 769px), print{.columns.is-variable.is-8-tablet{--columnGap: 2rem}}@media screen and (min-width: 769px) and (max-width: 1023px){.columns.is-variable.is-8-tablet-only{--columnGap: 2rem}}@media screen and (max-width: 1023px){.columns.is-variable.is-8-touch{--columnGap: 2rem}}@media screen and (min-width: 1024px){.columns.is-variable.is-8-desktop{--columnGap: 2rem}}@media screen and (min-width: 1024px) and (max-width: 1215px){.columns.is-variable.is-8-desktop-only{--columnGap: 2rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-8-widescreen{--columnGap: 2rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-8-widescreen-only{--columnGap: 2rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-8-fullhd{--columnGap: 2rem}}.tile{align-items:stretch;display:block;flex-basis:0;flex-grow:1;flex-shrink:1;min-height:-webkit-min-content;min-height:-moz-min-content;min-height:min-content}.tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.tile.is-ancestor:last-child{margin-bottom:-.75rem}.tile.is-ancestor:not(:last-child){margin-bottom:.75rem}.tile.is-child{margin:0 !important}.tile.is-parent{padding:.75rem}.tile.is-vertical{flex-direction:column}.tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem !important}@media screen and (min-width: 769px), print{.tile:not(.is-child){display:flex}.tile.is-1{flex:none;width:8.33333%}.tile.is-2{flex:none;width:16.66667%}.tile.is-3{flex:none;width:25%}.tile.is-4{flex:none;width:33.33333%}.tile.is-5{flex:none;width:41.66667%}.tile.is-6{flex:none;width:50%}.tile.is-7{flex:none;width:58.33333%}.tile.is-8{flex:none;width:66.66667%}.tile.is-9{flex:none;width:75%}.tile.is-10{flex:none;width:83.33333%}.tile.is-11{flex:none;width:91.66667%}.tile.is-12{flex:none;width:100%}}.hero{align-items:stretch;display:flex;flex-direction:column;justify-content:space-between}.hero .navbar{background:none}.hero .tabs ul{border-bottom:none}.hero.is-white{background-color:#fff;color:#0a0a0a}.hero.is-white a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-white strong{color:inherit}.hero.is-white .title{color:#0a0a0a}.hero.is-white .subtitle{color:rgba(10,10,10,0.9)}.hero.is-white .subtitle a:not(.button),.hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width: 1023px){.hero.is-white .navbar-menu{background-color:#fff}}.hero.is-white .navbar-item,.hero.is-white .navbar-link{color:rgba(10,10,10,0.7)}.hero.is-white a.navbar-item:hover,.hero.is-white a.navbar-item.is-active,.hero.is-white .navbar-link:hover,.hero.is-white .navbar-link.is-active{background-color:#f2f2f2;color:#0a0a0a}.hero.is-white .tabs a{color:#0a0a0a;opacity:0.9}.hero.is-white .tabs a:hover{opacity:1}.hero.is-white .tabs li.is-active a{opacity:1}.hero.is-white .tabs.is-boxed a,.hero.is-white .tabs.is-toggle a{color:#0a0a0a}.hero.is-white .tabs.is-boxed a:hover,.hero.is-white .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-white .tabs.is-boxed li.is-active a,.hero.is-white .tabs.is-boxed li.is-active a:hover,.hero.is-white .tabs.is-toggle li.is-active a,.hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.hero.is-white.is-bold{background-image:linear-gradient(141deg, #e6e6e6 0%, #fff 71%, #fff 100%)}@media screen and (max-width: 768px){.hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg, #e6e6e6 0%, #fff 71%, #fff 100%)}}.hero.is-black{background-color:#0a0a0a;color:#fff}.hero.is-black a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-black strong{color:inherit}.hero.is-black .title{color:#fff}.hero.is-black .subtitle{color:rgba(255,255,255,0.9)}.hero.is-black .subtitle a:not(.button),.hero.is-black .subtitle strong{color:#fff}@media screen and (max-width: 1023px){.hero.is-black .navbar-menu{background-color:#0a0a0a}}.hero.is-black .navbar-item,.hero.is-black .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-black a.navbar-item:hover,.hero.is-black a.navbar-item.is-active,.hero.is-black .navbar-link:hover,.hero.is-black .navbar-link.is-active{background-color:#000;color:#fff}.hero.is-black .tabs a{color:#fff;opacity:0.9}.hero.is-black .tabs a:hover{opacity:1}.hero.is-black .tabs li.is-active a{opacity:1}.hero.is-black .tabs.is-boxed a,.hero.is-black .tabs.is-toggle a{color:#fff}.hero.is-black .tabs.is-boxed a:hover,.hero.is-black .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-black .tabs.is-boxed li.is-active a,.hero.is-black .tabs.is-boxed li.is-active a:hover,.hero.is-black .tabs.is-toggle li.is-active a,.hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.hero.is-black.is-bold{background-image:linear-gradient(141deg, #000 0%, #0a0a0a 71%, #181616 100%)}@media screen and (max-width: 768px){.hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg, #000 0%, #0a0a0a 71%, #181616 100%)}}.hero.is-light{background-color:#f5f5f5;color:#363636}.hero.is-light a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-light strong{color:inherit}.hero.is-light .title{color:#363636}.hero.is-light .subtitle{color:rgba(54,54,54,0.9)}.hero.is-light .subtitle a:not(.button),.hero.is-light .subtitle strong{color:#363636}@media screen and (max-width: 1023px){.hero.is-light .navbar-menu{background-color:#f5f5f5}}.hero.is-light .navbar-item,.hero.is-light .navbar-link{color:rgba(54,54,54,0.7)}.hero.is-light a.navbar-item:hover,.hero.is-light a.navbar-item.is-active,.hero.is-light .navbar-link:hover,.hero.is-light .navbar-link.is-active{background-color:#e8e8e8;color:#363636}.hero.is-light .tabs a{color:#363636;opacity:0.9}.hero.is-light .tabs a:hover{opacity:1}.hero.is-light .tabs li.is-active a{opacity:1}.hero.is-light .tabs.is-boxed a,.hero.is-light .tabs.is-toggle a{color:#363636}.hero.is-light .tabs.is-boxed a:hover,.hero.is-light .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-light .tabs.is-boxed li.is-active a,.hero.is-light .tabs.is-boxed li.is-active a:hover,.hero.is-light .tabs.is-toggle li.is-active a,.hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.hero.is-light.is-bold{background-image:linear-gradient(141deg, #dfd8d9 0%, #f5f5f5 71%, #fff 100%)}@media screen and (max-width: 768px){.hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg, #dfd8d9 0%, #f5f5f5 71%, #fff 100%)}}.hero.is-dark{background-color:#363636;color:#f5f5f5}.hero.is-dark a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-dark strong{color:inherit}.hero.is-dark .title{color:#f5f5f5}.hero.is-dark .subtitle{color:rgba(245,245,245,0.9)}.hero.is-dark .subtitle a:not(.button),.hero.is-dark .subtitle strong{color:#f5f5f5}@media screen and (max-width: 1023px){.hero.is-dark .navbar-menu{background-color:#363636}}.hero.is-dark .navbar-item,.hero.is-dark .navbar-link{color:rgba(245,245,245,0.7)}.hero.is-dark a.navbar-item:hover,.hero.is-dark a.navbar-item.is-active,.hero.is-dark .navbar-link:hover,.hero.is-dark .navbar-link.is-active{background-color:#292929;color:#f5f5f5}.hero.is-dark .tabs a{color:#f5f5f5;opacity:0.9}.hero.is-dark .tabs a:hover{opacity:1}.hero.is-dark .tabs li.is-active a{opacity:1}.hero.is-dark .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a{color:#f5f5f5}.hero.is-dark .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.hero.is-dark.is-bold{background-image:linear-gradient(141deg, #1f191a 0%, #363636 71%, #46403f 100%)}@media screen and (max-width: 768px){.hero.is-dark.is-bold .navbar-menu{background-image:linear-gradient(141deg, #1f191a 0%, #363636 71%, #46403f 100%)}}.hero.is-primary{background-color:#00d1b2;color:#fff}.hero.is-primary a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-primary strong{color:inherit}.hero.is-primary .title{color:#fff}.hero.is-primary .subtitle{color:rgba(255,255,255,0.9)}.hero.is-primary .subtitle a:not(.button),.hero.is-primary .subtitle strong{color:#fff}@media screen and (max-width: 1023px){.hero.is-primary .navbar-menu{background-color:#00d1b2}}.hero.is-primary .navbar-item,.hero.is-primary .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-primary a.navbar-item:hover,.hero.is-primary a.navbar-item.is-active,.hero.is-primary .navbar-link:hover,.hero.is-primary .navbar-link.is-active{background-color:#00b89c;color:#fff}.hero.is-primary .tabs a{color:#fff;opacity:0.9}.hero.is-primary .tabs a:hover{opacity:1}.hero.is-primary .tabs li.is-active a{opacity:1}.hero.is-primary .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#00d1b2}.hero.is-primary.is-bold{background-image:linear-gradient(141deg, #009e6c 0%, #00d1b2 71%, #00e7eb 100%)}@media screen and (max-width: 768px){.hero.is-primary.is-bold .navbar-menu{background-image:linear-gradient(141deg, #009e6c 0%, #00d1b2 71%, #00e7eb 100%)}}.hero.is-link{background-color:#3273dc;color:#fff}.hero.is-link a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-link strong{color:inherit}.hero.is-link .title{color:#fff}.hero.is-link .subtitle{color:rgba(255,255,255,0.9)}.hero.is-link .subtitle a:not(.button),.hero.is-link .subtitle strong{color:#fff}@media screen and (max-width: 1023px){.hero.is-link .navbar-menu{background-color:#3273dc}}.hero.is-link .navbar-item,.hero.is-link .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-link a.navbar-item:hover,.hero.is-link a.navbar-item.is-active,.hero.is-link .navbar-link:hover,.hero.is-link .navbar-link.is-active{background-color:#2366d1;color:#fff}.hero.is-link .tabs a{color:#fff;opacity:0.9}.hero.is-link .tabs a:hover{opacity:1}.hero.is-link .tabs li.is-active a{opacity:1}.hero.is-link .tabs.is-boxed a,.hero.is-link .tabs.is-toggle a{color:#fff}.hero.is-link .tabs.is-boxed a:hover,.hero.is-link .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-link .tabs.is-boxed li.is-active a,.hero.is-link .tabs.is-boxed li.is-active a:hover,.hero.is-link .tabs.is-toggle li.is-active a,.hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#3273dc}.hero.is-link.is-bold{background-image:linear-gradient(141deg, #1577c6 0%, #3273dc 71%, #4366e5 100%)}@media screen and (max-width: 768px){.hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg, #1577c6 0%, #3273dc 71%, #4366e5 100%)}}.hero.is-info{background-color:#209cee;color:#fff}.hero.is-info a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-info strong{color:inherit}.hero.is-info .title{color:#fff}.hero.is-info .subtitle{color:rgba(255,255,255,0.9)}.hero.is-info .subtitle a:not(.button),.hero.is-info .subtitle strong{color:#fff}@media screen and (max-width: 1023px){.hero.is-info .navbar-menu{background-color:#209cee}}.hero.is-info .navbar-item,.hero.is-info .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-info a.navbar-item:hover,.hero.is-info a.navbar-item.is-active,.hero.is-info .navbar-link:hover,.hero.is-info .navbar-link.is-active{background-color:#118fe4;color:#fff}.hero.is-info .tabs a{color:#fff;opacity:0.9}.hero.is-info .tabs a:hover{opacity:1}.hero.is-info .tabs li.is-active a{opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#209cee}.hero.is-info.is-bold{background-image:linear-gradient(141deg, #04a6d7 0%, #209cee 71%, #3287f5 100%)}@media screen and (max-width: 768px){.hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg, #04a6d7 0%, #209cee 71%, #3287f5 100%)}}.hero.is-success{background-color:#23d160;color:#fff}.hero.is-success a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-success strong{color:inherit}.hero.is-success .title{color:#fff}.hero.is-success .subtitle{color:rgba(255,255,255,0.9)}.hero.is-success .subtitle a:not(.button),.hero.is-success .subtitle strong{color:#fff}@media screen and (max-width: 1023px){.hero.is-success .navbar-menu{background-color:#23d160}}.hero.is-success .navbar-item,.hero.is-success .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-success a.navbar-item:hover,.hero.is-success a.navbar-item.is-active,.hero.is-success .navbar-link:hover,.hero.is-success .navbar-link.is-active{background-color:#20bc56;color:#fff}.hero.is-success .tabs a{color:#fff;opacity:0.9}.hero.is-success .tabs a:hover{opacity:1}.hero.is-success .tabs li.is-active a{opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#23d160}.hero.is-success.is-bold{background-image:linear-gradient(141deg, #12af2f 0%, #23d160 71%, #2ce28a 100%)}@media screen and (max-width: 768px){.hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg, #12af2f 0%, #23d160 71%, #2ce28a 100%)}}.hero.is-warning{background-color:#ffdd57;color:rgba(0,0,0,0.7)}.hero.is-warning a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-warning strong{color:inherit}.hero.is-warning .title{color:rgba(0,0,0,0.7)}.hero.is-warning .subtitle{color:rgba(0,0,0,0.9)}.hero.is-warning .subtitle a:not(.button),.hero.is-warning .subtitle strong{color:rgba(0,0,0,0.7)}@media screen and (max-width: 1023px){.hero.is-warning .navbar-menu{background-color:#ffdd57}}.hero.is-warning .navbar-item,.hero.is-warning .navbar-link{color:rgba(0,0,0,0.7)}.hero.is-warning a.navbar-item:hover,.hero.is-warning a.navbar-item.is-active,.hero.is-warning .navbar-link:hover,.hero.is-warning .navbar-link.is-active{background-color:#ffd83d;color:rgba(0,0,0,0.7)}.hero.is-warning .tabs a{color:rgba(0,0,0,0.7);opacity:0.9}.hero.is-warning .tabs a:hover{opacity:1}.hero.is-warning .tabs li.is-active a{opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,0.7)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,0.7);border-color:rgba(0,0,0,0.7);color:#ffdd57}.hero.is-warning.is-bold{background-image:linear-gradient(141deg, #ffaf24 0%, #ffdd57 71%, #fffa70 100%)}@media screen and (max-width: 768px){.hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg, #ffaf24 0%, #ffdd57 71%, #fffa70 100%)}}.hero.is-danger{background-color:#ff3860;color:#fff}.hero.is-danger a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-danger strong{color:inherit}.hero.is-danger .title{color:#fff}.hero.is-danger .subtitle{color:rgba(255,255,255,0.9)}.hero.is-danger .subtitle a:not(.button),.hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width: 1023px){.hero.is-danger .navbar-menu{background-color:#ff3860}}.hero.is-danger .navbar-item,.hero.is-danger .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-danger a.navbar-item:hover,.hero.is-danger a.navbar-item.is-active,.hero.is-danger .navbar-link:hover,.hero.is-danger .navbar-link.is-active{background-color:#ff1f4b;color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:0.9}.hero.is-danger .tabs a:hover{opacity:1}.hero.is-danger .tabs li.is-active a{opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#ff3860}.hero.is-danger.is-bold{background-image:linear-gradient(141deg, #ff0561 0%, #ff3860 71%, #ff5257 100%)}@media screen and (max-width: 768px){.hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg, #ff0561 0%, #ff3860 71%, #ff5257 100%)}}.hero.is-small .hero-body{padding-bottom:1.5rem;padding-top:1.5rem}@media screen and (min-width: 769px), print{.hero.is-medium .hero-body{padding-bottom:9rem;padding-top:9rem}}@media screen and (min-width: 769px), print{.hero.is-large .hero-body{padding-bottom:18rem;padding-top:18rem}}.hero.is-halfheight .hero-body,.hero.is-fullheight .hero-body,.hero.is-fullheight-with-navbar .hero-body{align-items:center;display:flex}.hero.is-halfheight .hero-body>.container,.hero.is-fullheight .hero-body>.container,.hero.is-fullheight-with-navbar .hero-body>.container{flex-grow:1;flex-shrink:1}.hero.is-halfheight{min-height:50vh}.hero.is-fullheight{min-height:100vh}.hero-video{overflow:hidden}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;-webkit-transform:translate3d(-50%, -50%, 0);transform:translate3d(-50%, -50%, 0)}.hero-video.is-transparent{opacity:0.3}@media screen and (max-width: 768px){.hero-video{display:none}}.hero-buttons{margin-top:1.5rem}@media screen and (max-width: 768px){.hero-buttons .button{display:flex}.hero-buttons .button:not(:last-child){margin-bottom:0.75rem}}@media screen and (min-width: 769px), print{.hero-buttons{display:flex;justify-content:center}.hero-buttons .button:not(:last-child){margin-right:1.5rem}}.hero-head,.hero-foot{flex-grow:0;flex-shrink:0}.hero-body{flex-grow:1;flex-shrink:0;padding:3rem 1.5rem}.section{padding:3rem 1.5rem}@media screen and (min-width: 1024px){.section.is-medium{padding:9rem 1.5rem}.section.is-large{padding:18rem 1.5rem}}.footer{background-color:#fafafa;padding:3rem 1.5rem 6rem}html{height:100%;width:100%}body{font-family:"TT Norms Medium",sans-serif;font-size:15px;position:relative;height:100%;width:100%;overflow-x:hidden}body.small-header .top-nav{height:75px}body.small-header #main{padding-top:75px}.top-nav{height:120px}@media screen and (max-width: 1023px){.top-nav{height:75px}}#main{height:100%;display:flex;flex-direction:row;background:#F8F8F9;padding-top:120px;overflow-x:hidden;width:100%}@media screen and (max-width: 1023px){#main{padding-top:75px}}#main>.sidebar{padding:40px 30px;flex-grow:0;flex-shrink:0;width:240px;border-right:1px solid #EAEAF1;height:100%;overflow:auto}#main>.sidebar.tutorials{width:320px}#main>.core{padding:28px;height:100%;overflow:auto;flex-grow:1}@media screen and (max-width: 768px){#main>.core{padding:0px}}#main>.core>.content{background:#fff;padding:40px;border-radius:4px;box-shadow:0 0 40px 0 rgba(115,134,160,0.24)}#main>.side-nav{width:240px;padding:40px 20px;flex-grow:0;flex-shrink:0;height:100%;border-left:1px solid #EAEAF1;overflow:auto}.content{margin-bottom:50px}.content blockquote{margin:30px 0 !important}.content .signature-attributes{margin-left:8px;margin-right:3px;font-style:italic}.content header.page-title p{font-size:13px;margin:0 0 5px;text-transform:uppercase}.content header p{font-size:20px}.content h1,.content header.page-title h1{font-family:"TT Norms Medium",sans-serif;font-size:47px;font-weight:bold;margin:8px 0}.content h2{font-size:26px;line-height:48px;font-weight:bold;margin-bottom:26px}.content h3,.content h4,.content h5,.content h6{font-family:"TT Norms Medium",sans-serif;font-weight:900;letter-spacing:0}.content code{color:#101010;font-family:"Inconsolata",monospace}.content .container-overview .prettyprint:last-child{margin-bottom:50px}.content .vertical-section{padding:16px 0}@media screen and (max-width: 1023px){#main-content-wrapper{padding:0 30px}}body.landing>.top-nav{box-shadow:none;transition:margin-top 0.3s;color:#fff;background:#4268F6}body.landing>.top-nav.hidden{transition:margin-top 0.3s;margin-top:-130px}@media screen and (max-width: 1023px){body.landing>.top-nav.hidden{margin-top:-85px}}body.landing>.top-nav.sticky{box-shadow:0 0 20px 0 rgba(0,0,255,0.5)}body.landing>.top-nav .inner{margin:0 auto;max-width:1226px}body.landing>.top-nav a.button{color:#fff;border-color:#fff;background:transparent}body.landing>.top-nav a.button:hover{background:#fff;border-color:#fff;color:#192035}body.landing>.top-nav .menu .navigation a.link{color:#fff}body.landing>.top-nav .menu .navigation a.link:hover{border-color:#fff}body.landing>.top-nav .image img{content:url("../images/logo.svg")}body.landing>.top-nav #hamburger{display:none}body.landing #main{display:block;height:auto}body.landing .main-hero{background:#4268F6;color:#fff;padding:300px 40% 160px;border-bottom-left-radius:50%;border-bottom-right-radius:50%;margin:-250px -30% 0;text-align:center}body.landing .main-hero .action-buttons{margin:60px 0;vertical-align:middle}body.landing .main-hero .action-buttons span{color:#fff}body.landing .main-hero h3{font-size:24px;line-height:65px;font-weight:lighter}body.landing .main-hero h1{font-size:52px;line-height:65px;font-weight:lighter;max-width:900px;margin-left:auto;margin-right:auto}body.landing .main-hero strong{color:#fff}body.landing .gif-box{margin-top:-140px;text-align:center}body.landing .grey-logos{text-align:center;margin-bottom:50px}body.landing .grey-logos .column{display:flex;align-items:center;justify-content:center}body.landing .white-oval{background:#fff;padding:110px 500px 100px;margin:0 -500px;text-align:center;border-bottom-left-radius:50%;border-bottom-right-radius:50%}body.landing h2{font-weight:bold;font-size:36px;line-height:48px;color:#101010;margin-bottom:15px}body.landing h2+p{color:#767676;font-size:16px}body.landing h4{font-weight:bold;font-size:24px;line-height:32px}body.landing .header-message{margin-bottom:80px}body.landing .todo-actions{text-align:left;padding:100px 0 100px 100px}@media screen and (max-width: 1215px){body.landing .todo-actions{padding-top:20px}}body.landing .todo-actions h4{margin-bottom:60px;position:relative}body.landing .todo-actions h4:before{content:'';position:absolute;left:-50px;top:0;height:30px;width:30px;background:url("../images/check.svg") no-repeat 50% 50%}body.landing .action-buttons span{line-height:36px;margin:0 10px;color:#4268F6}body.landing .credentials{text-align:center;padding:100px 0;background:url("../images/map.svg") no-repeat 50% 50%}body.landing .credentials .fa-youtube{color:#FF0000}body.landing .credentials .fa-reddit{color:#FF4500}body.landing .credentials .fa-github{color:#101010}body.landing .credentials .columns{margin-bottom:20px}body.landing .credentials .column{display:flex;flex-direction:column}body.landing .credentials .box{flex-direction:column;padding:30px;height:100%;box-shadow:0px 0px 40px rgba(115,134,160,0.25);display:flex;justify-content:center}body.landing .credentials .box:hover{box-shadow:0px 0px 40px rgba(115,134,160,0.5)}body.landing .credentials .box h5{align-self:center;font-size:22px;line-height:26px;margin-bottom:30px}body.landing .credentials .box span{font-size:11px}body.landing .stat-box{padding:40px 65px;box-shadow:0px 0px 40px rgba(115,134,160,0.25)}@media screen and (max-width: 768px){body.landing .stat-box .column:first-child{padding-bottom:40px}}body.landing .stat-box .fa-github{color:#101010}body.landing .stat-box h2{margin:0 0 60px}body.landing .stat-box h4{font-size:32px;font-weight:bolder;margin-top:15px;color:#101010}body.landing .stat-box h4 strong{color:#101010}body.landing .stat-box .level{border-bottom:#4C73F7 3px solid;margin-bottom:-3px}body.landing .stat-box .level img{position:relative;bottom:-3px}body.landing .stat-box .action-buttons{margin:50px 0 0}body.landing .feature-docs{margin-top:-200px;padding-top:300px}@media screen and (min-width: 1216px){body.landing .feature-docs .container .columns.is-multiline{margin:0 8.333%}}body.landing .feature-docs .columns.is-multiline .column{display:flex}body.landing .feature-docs .box{color:#101010}body.landing .feature-docs .box:hover{box-shadow:4px 8px 12px rgba(115,134,160,0.25)}body.landing .feature-docs .box img{margin:-10px 0}body.landing .feature-docs .box h4{line-height:36px;font-size:26px}body.landing .feature-docs .box p{font-size:20px;line-height:26px;margin:35px 0}body.landing .feature-docs .action-buttons{margin:100px 0 50px}body.landing .feature-side-blocks .bg-crud{background:url("../images/bg-crud.png") no-repeat 100% 50%}@media screen and (max-width: 1215px){body.landing .feature-side-blocks .bg-crud{background-position-x:150%}}@media screen and (max-width: 1023px){body.landing .feature-side-blocks .bg-crud{background:none;text-align:center}}body.landing .feature-side-blocks .bg-filter{background:url("../images/bg-filter.png") no-repeat 0% 50%}@media screen and (max-width: 1215px){body.landing .feature-side-blocks .bg-filter{background-position-x:-200px}}@media screen and (max-width: 1023px){body.landing .feature-side-blocks .bg-filter{background:none;text-align:center}}body.landing .feature-side-blocks .column{justify-content:center;display:flex;flex-direction:column}@media screen and (min-width: 1024px){body.landing .feature-side-blocks .column{height:700px}}body.landing .feature-side-blocks .container{margin-top:50px;margin-bottom:50px}body.landing .feature-side-blocks .action-buttons{margin:30px 0}body.landing .support-block{padding:80px 0 350px;background:#fff;margin-bottom:-200px}body.landing .support-block .column{display:flex;flex-direction:column}body.landing .support-block .column .box{flex-grow:1}body.landing .support-block .img{text-align:center;border-bottom:1px solid #D8D8D8;margin:0 -20px 20px}body.landing .support-block h4{font-weight:bolder;font-size:26px;line-height:48px}body.landing .support-block .text{padding:0 10px 20px}body.landing .support-block .form{background:#4268F6;padding:40px;border-radius:10px}body.landing .button.is-success{width:170px;height:50px}body.landing .form{color:#fff}body.landing .form .success-msg{display:none}body.landing .form .success-msg img{width:150px;margin:100px 0}body.landing .form.completed .success-msg{display:block}body.landing .form.completed .form-fields{display:none}body.landing .form h2{color:#fff;margin-bottom:30px}body.landing .form .label{font-size:20px}body.landing .form .field{margin-bottom:40px;color:#fff}body.landing .form .field label{color:#fff;font-weight:bold}body.landing .form .field input{border-radius:4px;height:54px}body.landing .form .checkbox{display:block;padding:8px 0;font-size:16px}body.landing .form .checkbox:hover{color:#fff}body.landing .form .checkbox input{margin-right:5px}body.landing .form .interested{padding:0 0 20px}body.landing .form .interested .label{color:#fff}body.landing .form textarea{height:80px}body.landing .form .notice{font-size:14px;font-weight:lighter;padding:10px 30px}body.landing .curved-footer{background:#4268F6;padding:110px 500px;margin:0 -500px;border-top-left-radius:50%;border-top-right-radius:50%;color:#fff}body.landing .curved-footer .the-part{position:relative}body.landing .curved-footer .the-part h2{font-size:90px;line-height:120px;opacity:0.08;color:#fff}@media screen and (max-width: 1023px){body.landing .curved-footer .the-part h2{font-size:70px}}body.landing .curved-footer .the-part h4{font-size:56px;line-height:65px;position:absolute;left:0;top:0;right:0;padding:80px 0}body.landing .button.is-link{background:transparent}body.landing .button.is-link span{border-bottom:1px solid #fff}body.landing .button.is-link:hover span{border-bottom:none}body.landing .top{border-bottom:1px solid rgba(255,255,255,0.2);padding-bottom:120px;text-align:center}body.landing .bottom{font-size:14px;padding:65px 0 0}@media screen and (max-width: 768px){body.landing .bottom{padding:20px}}body.landing .bottom strong{color:#fff}body.landing .bottom p{padding:6px 0}body.landing .bottom a{color:#fff}body.landing .bottom .sb{padding-top:40px}body.landing .bottom .logo{padding-bottom:30px}body.landing .bottom .button.is-success{margin-top:40px;height:54px}body.landing .bottom .form{margin-top:30px}.top-nav{background:#fff;padding:8px 24px;box-shadow:0 0 40px 0 rgba(115,134,160,0.24);position:fixed;top:0;left:0;right:0;z-index:5}@media screen and (max-width: 768px){.top-nav{padding:8px}}.top-nav h1{font-size:20px}.top-nav .inner{display:flex;align-items:center}.top-nav #hamburger{margin-left:0}@media screen and (max-width: 768px){.top-nav .logo{display:none}}.top-nav .menu{flex-grow:1}.top-nav .menu .top-buttons{text-align:right;margin-bottom:8px;margin-top:2px}@media screen and (max-width: 1023px){.top-nav .menu .top-buttons{display:none}}.top-nav .menu .top-buttons .button{margin-left:16px}.top-nav .menu .navigation{text-align:right;margin-bottom:4px}.top-nav .menu .navigation .link{border:none;display:inline-block;padding:4px 8px;color:#101010;margin-right:2px;line-height:48px;height:48px;vertical-align:middle;height:46px}.top-nav .menu .navigation .link:hover:not(.no-hover){border-bottom:2px solid #101010}@media screen and (max-width: 768px){.top-nav .menu .navigation .link.user-link{display:none}}.sidebar{padding-bottom:120px}.sidebar .search-wrapper{margin:-20px -15px 21px}.sidebar .search-wrapper input{border-radius:0}.sidebar a{color:#798897;overflow-wrap:break-word}.sidebar a:hover,.sidebar a.active{color:#E6282B}.sidebar h3{margin:1.6rem 0 .4rem;color:#211D1A;font-size:12px;text-transform:uppercase}.sidebar ul{padding:0 0 .26667rem 1.06667rem}.sidebar ul li{padding:.2rem 0}.sidebar li>ul{padding:0 0 0px 25px}.sidebar .category h2{color:#000;font-size:20px;margin-top:40px}#sidebarNav.sticky{left:0;transition:left 0.5s}@media screen and (max-width: 1023px){#sidebarNav{z-index:100;top:0;left:-300px;position:fixed;transition:left 0.5s;padding:28px;width:300px;bottom:0;overflow:auto;background:#fff}#sidebarNav .sidebar{padding-bottom:10px}}#stickyNavbarOverlay{position:absolute;left:0;right:0;bottom:0;top:0;z-index:40;background:rgba(0,0,0,0.2);display:none}#stickyNavbarOverlay.active{display:block}.side-nav a{color:#798897;overflow-wrap:break-word}.side-nav a:hover,.side-nav a.is-active{color:#E6282B}.side-nav a.is-past{opacity:0.7}.side-nav h3{margin:1.6rem 0 .4rem;color:#211D1A;font-size:12px;text-transform:uppercase}.side-nav ul{padding:0 0 .26667rem 1.06667rem}.side-nav ul li{padding:.2rem 0}@media screen and (max-width: 768px){.side-nav{display:none}}.footer{border-top:1px solid #EAEAF1;padding:20px;margin:0 -30px -30px;background:#F8F8F9}.footer .content{margin-bottom:0}.footer .fas{color:#E6282B}.footer a{font-weight:bold}.footer a:hover{color:#E6282B}.members{margin-top:24px}.member:not(:last-child):after{content:"";background:#EAEAF1;height:2px;display:block;margin:45px -40px 40px}.member>.is-pulled-right{position:relative;z-index:2}.member>.name{color:#211D1A;font-size:20px;line-height:26px;position:relative;margin-bottom:8px}.member>.name .code-name{font-family:"Inconsolata",monospace;display:block;font-size:25px;line-height:30px;margin-top:8px}.member>.name .code-name:first-child{margin-left:0}.member>.name .tag{position:relative;top:-1px;margin-right:3px}.member>.name .href-link{color:#211D1A;position:absolute;padding:1px;left:-20px;top:0;bottom:0;width:21px;opacity:0}.member>.name:hover .href-link{opacity:1}.member h5{font-size:20px}.member>.description{margin-bottom:25px}.member>.description p{font-size:20px;margin:25px 0}table.params,table.props{border:1px solid #EAEAF1;line-height:26px}table.params thead,table.props thead{border:none}table.params thead th,table.props thead th{font-weight:normal;padding:13px 26px}table.params tr,table.props tr{border-bottom:1px solid #EAEAF1}table.params td,table.props td{padding:13px 26px}table.params td.name code,table.props td.name code{background:transparent;padding:0;font-size:15px;color:#211D1A}table.params tr.deep-level-1,table.props tr.deep-level-1{background:#fafafa}table.params tr.deep-level-1 .name code,table.props tr.deep-level-1 .name code{padding-left:25px;margin-left:0px;border-left:1px solid #DEE1E5}table.params tr.deep-level-2,table.props tr.deep-level-2{background:#f5f5f5}table.params tr.deep-level-2 .name code,table.props tr.deep-level-2 .name code{padding-left:25px;margin-left:25px;border-left:1px solid #DEE1E5}table.params tr.deep-level-3,table.props tr.deep-level-3{background:#f0f0f0}table.params tr.deep-level-3 .name code,table.props tr.deep-level-3 .name code{padding-left:25px;margin-left:50px;border-left:1px solid #DEE1E5}table.params tr.deep-level-4,table.props tr.deep-level-4{background:#ebebeb}table.params tr.deep-level-4 .name code,table.props tr.deep-level-4 .name code{padding-left:25px;margin-left:75px;border-left:1px solid #DEE1E5}table.params tr.deep-level-5,table.props tr.deep-level-5{background:#e6e6e6}table.params tr.deep-level-5 .name code,table.props tr.deep-level-5 .name code{padding-left:25px;margin-left:100px;border-left:1px solid #DEE1E5}table.params tr.deep-level-6,table.props tr.deep-level-6{background:#e0e0e0}table.params tr.deep-level-6 .name code,table.props tr.deep-level-6 .name code{padding-left:25px;margin-left:125px;border-left:1px solid #DEE1E5}table.params tr.deep-level-7,table.props tr.deep-level-7{background:#dbdbdb}table.params tr.deep-level-7 .name code,table.props tr.deep-level-7 .name code{padding-left:25px;margin-left:150px;border-left:1px solid #DEE1E5}table.params tr.deep-level-8,table.props tr.deep-level-8{background:#d6d6d6}table.params tr.deep-level-8 .name code,table.props tr.deep-level-8 .name code{padding-left:25px;margin-left:175px;border-left:1px solid #DEE1E5}table.params tr.deep-level-9,table.props tr.deep-level-9{background:#d1d1d1}table.params tr.deep-level-9 .name code,table.props tr.deep-level-9 .name code{padding-left:25px;margin-left:200px;border-left:1px solid #DEE1E5}table.params tr.deep-level-10,table.props tr.deep-level-10{background:#ccc}table.params tr.deep-level-10 .name code,table.props tr.deep-level-10 .name code{padding-left:25px;margin-left:225px;border-left:1px solid #DEE1E5}.prettyprint{border-radius:2px;background-color:#2F4858}.prettyprint code{font-family:"Inconsolata",monospace}pre.prettyprint li.L0,pre.prettyprint li.L1,pre.prettyprint li.L2,pre.prettyprint li.L3,pre.prettyprint li.L4,pre.prettyprint li.L5,pre.prettyprint li.L6,pre.prettyprint li.L7,pre.prettyprint li.L8,pre.prettyprint li.L9{background:none}.button{transition:all 0.2s;border-radius:4px;padding:8px 24px;height:40px;border-color:#4268F6;color:#4268F6}.button:hover{color:#535B8E;border-color:#535B8E;transition:all 0.2s}.button.is-primary{background-color:#4268F6}.button.is-primary:hover{background-color:#535B8E}.button.is-primary.is-outlined{border-color:#4268F6;color:#4268F6}.button.is-primary.is-outlined:hover{border-color:#535B8E;color:#535B8E;background:transparent}.button.is-success{background:#69D6D4}.button.is-white.is-outlined{background:transparent;border-color:#fff}.button>i:first-child{margin-right:8px;margin-left:-8px}.tag-source{margin:28px 0}.tag-source span{display:inline-block;padding:13px 14px}.tag-source span a{color:#EAEAF1}.tag-source span a:hover{color:#798897}.method-parameter{font-size:20px}.method-parameter label{color:18px}.method-parameter ul{margin:0 0 0 25px}.mermaid .edgeLabel{background:white;color:#2F4858;font-size:15px;font-weight:normal}.mermaid .node circle,.mermaid .node ellipse,.mermaid .node polygon,.mermaid .node rect{fill:rgba(248,249,250,0.8) !important;stroke:rgba(121,136,151,0.6) !important}.mermaid .cluster rect{fill:rgba(125,132,255,0.1) !important;stroke:rgba(125,132,255,0.5) !important}.mermaid .node g.label{color:#2F4858}.mermaid .node g.label div{font-weight:normal;font-size:15px}.tag{text-transform:uppercase}.details dt{font-size:20px;border-left:2px solid #008DDF;padding-left:16px} diff --git a/docs/styles/iframe.css b/docs/styles/iframe.css new file mode 100644 index 0000000..84dec06 --- /dev/null +++ b/docs/styles/iframe.css @@ -0,0 +1,13 @@ +.bd__button { + padding: 10px 0; + text-align: right; +} +.bd__button > a{ + font-weight: 100; + text-decoration: none; + color: #BDC3CB; + font-family: sans-serif; +} +.bd__button > a:hover { + color: #798897; +} \ No newline at end of file diff --git a/docs/styles/jsdoc-default.css b/docs/styles/jsdoc-default.css new file mode 100644 index 0000000..7d1729d --- /dev/null +++ b/docs/styles/jsdoc-default.css @@ -0,0 +1,358 @@ +@font-face { + font-family: 'Open Sans'; + font-weight: normal; + font-style: normal; + src: url('../fonts/OpenSans-Regular-webfont.eot'); + src: + local('Open Sans'), + local('OpenSans'), + url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-Regular-webfont.woff') format('woff'), + url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg'); +} + +@font-face { + font-family: 'Open Sans Light'; + font-weight: normal; + font-style: normal; + src: url('../fonts/OpenSans-Light-webfont.eot'); + src: + local('Open Sans Light'), + local('OpenSans Light'), + url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-Light-webfont.woff') format('woff'), + url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg'); +} + +html +{ + overflow: auto; + background-color: #fff; + font-size: 14px; +} + +body +{ + font-family: 'Open Sans', sans-serif; + line-height: 1.5; + color: #4d4e53; + background-color: white; +} + +a, a:visited, a:active { + color: #0095dd; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +header +{ + display: block; + padding: 0px 4px; +} + +tt, code, kbd, samp { + font-family: Consolas, Monaco, 'Andale Mono', monospace; +} + +.class-description { + font-size: 130%; + line-height: 140%; + margin-bottom: 1em; + margin-top: 1em; +} + +.class-description:empty { + margin: 0; +} + +#main { + float: left; + width: 70%; +} + +article dl { + margin-bottom: 40px; +} + +article img { + max-width: 100%; +} + +section +{ + display: block; + background-color: #fff; + padding: 12px 24px; + border-bottom: 1px solid #ccc; + margin-right: 30px; +} + +.variation { + display: none; +} + +.signature-attributes { + font-size: 60%; + color: #aaa; + font-style: italic; + font-weight: lighter; +} + +nav +{ + display: block; + float: right; + margin-top: 28px; + width: 30%; + box-sizing: border-box; + border-left: 1px solid #ccc; + padding-left: 16px; +} + +nav ul { + font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; + font-size: 100%; + line-height: 17px; + padding: 0; + margin: 0; + list-style-type: none; +} + +nav ul a, nav ul a:visited, nav ul a:active { + font-family: Consolas, Monaco, 'Andale Mono', monospace; + line-height: 18px; + color: #4D4E53; +} + +nav h3 { + margin-top: 12px; +} + +nav li { + margin-top: 6px; +} + +footer { + display: block; + padding: 6px; + margin-top: 12px; + font-style: italic; + font-size: 90%; +} + +h1, h2, h3, h4 { + font-weight: 200; + margin: 0; +} + +h1 +{ + font-family: 'Open Sans Light', sans-serif; + font-size: 48px; + letter-spacing: -2px; + margin: 12px 24px 20px; +} + +h2, h3.subsection-title +{ + font-size: 30px; + font-weight: 700; + letter-spacing: -1px; + margin-bottom: 12px; +} + +h3 +{ + font-size: 24px; + letter-spacing: -0.5px; + margin-bottom: 12px; +} + +h4 +{ + font-size: 18px; + letter-spacing: -0.33px; + margin-bottom: 12px; + color: #4d4e53; +} + +h5, .container-overview .subsection-title +{ + font-size: 120%; + font-weight: bold; + letter-spacing: -0.01em; + margin: 8px 0 3px 0; +} + +h6 +{ + font-size: 100%; + letter-spacing: -0.01em; + margin: 6px 0 3px 0; + font-style: italic; +} + +table +{ + border-spacing: 0; + border: 0; + border-collapse: collapse; +} + +td, th +{ + border: 1px solid #ddd; + margin: 0px; + text-align: left; + vertical-align: top; + padding: 4px 6px; + display: table-cell; +} + +thead tr +{ + background-color: #ddd; + font-weight: bold; +} + +th { border-right: 1px solid #aaa; } +tr > th:last-child { border-right: 1px solid #ddd; } + +.ancestors, .attribs { color: #999; } +.ancestors a, .attribs a +{ + color: #999 !important; + text-decoration: none; +} + +.clear +{ + clear: both; +} + +.important +{ + font-weight: bold; + color: #950B02; +} + +.yes-def { + text-indent: -1000px; +} + +.type-signature { + color: #aaa; +} + +.name, .signature { + font-family: Consolas, Monaco, 'Andale Mono', monospace; +} + +.details { margin-top: 14px; border-left: 2px solid #DDD; } +.details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } +.details dd { margin-left: 70px; } +.details ul { margin: 0; } +.details ul { list-style-type: none; } +.details li { margin-left: 30px; padding-top: 6px; } +.details pre.prettyprint { margin: 0 } +.details .object-value { padding-top: 0; } + +.description { + margin-bottom: 1em; + margin-top: 1em; +} + +.code-caption +{ + font-style: italic; + font-size: 107%; + margin: 0; +} + +.source +{ + border: 1px solid #ddd; + width: 80%; + overflow: auto; +} + +.prettyprint.source { + width: inherit; +} + +.source code +{ + font-size: 100%; + line-height: 18px; + display: block; + padding: 4px 12px; + margin: 0; + background-color: #fff; + color: #4D4E53; +} + +.prettyprint code span.line +{ + display: inline-block; +} + +.prettyprint.linenums +{ + padding-left: 70px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.prettyprint.linenums ol +{ + padding-left: 0; +} + +.prettyprint.linenums li +{ + border-left: 3px #ddd solid; +} + +.prettyprint.linenums li.selected, +.prettyprint.linenums li.selected * +{ + background-color: lightyellow; +} + +.prettyprint.linenums li * +{ + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.params .name, .props .name, .name code { + color: #4D4E53; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 100%; +} + +.params td.description > p:first-child, +.props td.description > p:first-child +{ + margin-top: 0; + padding-top: 0; +} + +.params td.description > p:last-child, +.props td.description > p:last-child +{ + margin-bottom: 0; + padding-bottom: 0; +} + +.disabled { + color: #454545; +} diff --git a/docs/styles/prettify-jsdoc.css b/docs/styles/prettify-jsdoc.css new file mode 100644 index 0000000..5a2526e --- /dev/null +++ b/docs/styles/prettify-jsdoc.css @@ -0,0 +1,111 @@ +/* JSDoc prettify.js theme */ + +/* plain text */ +.pln { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* string content */ +.str { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a keyword */ +.kwd { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a comment */ +.com { + font-weight: normal; + font-style: italic; +} + +/* a type name */ +.typ { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a literal value */ +.lit { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* punctuation */ +.pun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp open bracket */ +.opn { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp close bracket */ +.clo { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a markup tag name */ +.tag { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute name */ +.atn { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute value */ +.atv { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a declaration */ +.dec { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a variable name */ +.var { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a function name */ +.fun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; +} diff --git a/docs/styles/prettify-tomorrow.css b/docs/styles/prettify-tomorrow.css new file mode 100644 index 0000000..b6f92a7 --- /dev/null +++ b/docs/styles/prettify-tomorrow.css @@ -0,0 +1,132 @@ +/* Tomorrow Theme */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* Pretty printing styles. Used with prettify.js. */ +/* SPAN elements with the classes below are added by prettyprint. */ +/* plain text */ +.pln { + color: #4d4d4c; } + +@media screen { + /* string content */ + .str { + color: #718c00; } + + /* a keyword */ + .kwd { + color: #8959a8; } + + /* a comment */ + .com { + color: #8e908c; } + + /* a type name */ + .typ { + color: #4271ae; } + + /* a literal value */ + .lit { + color: #f5871f; } + + /* punctuation */ + .pun { + color: #4d4d4c; } + + /* lisp open bracket */ + .opn { + color: #4d4d4c; } + + /* lisp close bracket */ + .clo { + color: #4d4d4c; } + + /* a markup tag name */ + .tag { + color: #c82829; } + + /* a markup attribute name */ + .atn { + color: #f5871f; } + + /* a markup attribute value */ + .atv { + color: #3e999f; } + + /* a declaration */ + .dec { + color: #f5871f; } + + /* a variable name */ + .var { + color: #c82829; } + + /* a function name */ + .fun { + color: #4271ae; } } +/* Use higher contrast and text-weight for printable form. */ +@media print, projection { + .str { + color: #060; } + + .kwd { + color: #006; + font-weight: bold; } + + .com { + color: #600; + font-style: italic; } + + .typ { + color: #404; + font-weight: bold; } + + .lit { + color: #044; } + + .pun, .opn, .clo { + color: #440; } + + .tag { + color: #006; + font-weight: bold; } + + .atn { + color: #404; } + + .atv { + color: #060; } } +/* Style */ +/* +pre.prettyprint { + background: white; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 12px; + line-height: 1.5; + border: 1px solid #ccc; + padding: 10px; } +*/ + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; } + +/* IE indents via margin-left */ +li.L0, +li.L1, +li.L2, +li.L3, +li.L4, +li.L5, +li.L6, +li.L7, +li.L8, +li.L9 { + /* */ } + +/* Alternate shading for lines */ +li.L1, +li.L3, +li.L5, +li.L7, +li.L9 { + /* */ } diff --git a/docs/styles/reset.css b/docs/styles/reset.css new file mode 100644 index 0000000..5a808c7 --- /dev/null +++ b/docs/styles/reset.css @@ -0,0 +1,44 @@ +/* reset css */ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/docs/wasc-worker_WascBuilderPlugin.js.html b/docs/wasc-worker_WascBuilderPlugin.js.html new file mode 100644 index 0000000..caf1de3 --- /dev/null +++ b/docs/wasc-worker_WascBuilderPlugin.js.html @@ -0,0 +1,322 @@ + + + + + + + + + + wasc-worker/WascBuilderPlugin.js + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Source

    +

    wasc-worker/WascBuilderPlugin.js

    +
    + + + + + +
    +
    +
    /**
    +* @author hexxone / https://hexx.one
    +*
    +* @license
    +* Copyright (c) 2021 hexxone All rights reserved.
    +* Licensed under the GNU GENERAL PUBLIC LICENSE.
    +* See LICENSE file in the project root for full license information.
    +* @ignore
    +*/
    +
    +
    +const fs = require('fs');
    +const path = require('path');
    +
    +const asc = require('assemblyscript/bin/asc');
    +const validate = require('schema-utils');
    +const {RawSource} = require('webpack-sources');
    +const {Compilation} = require('webpack');
    +
    +const pluginName = 'WasmPlugin';
    +const outPath = path.resolve(__dirname, 'build');
    +
    +/**
    +* schema for options object
    +* @see {WascBuilderPlugin}
    +*/
    +const wascSchema = {
    +	type: 'object',
    +	properties: {
    +		production: {
    +			type: 'boolean',
    +		},
    +		relpath: {
    +			type: 'string',
    +		},
    +		extension: {
    +			type: 'string',
    +		},
    +		cleanup: {
    +			type: 'boolean',
    +		},
    +	},
    +};
    +
    +/**
    +* This is a webpack plugin
    +*/
    +class WascBuilderPlugin {
    +	options = {};
    +
    +	/**
    +	* Intializes the plugin in the webpack build process
    +	* @param {wascSchema} options
    +	*/
    +	constructor(options = {}) {
    +		validate.validate(wascSchema, options);
    +		this.options = options;
    +	}
    +
    +	/**
    +	* @ignore
    +	* Hook into the compilation process,
    +	* find all target files by a regex
    +	* then compile and add them to the webpack compiled files.
    +	* @param {Webpack.compiler} compiler object from webpack
    +	*/
    +	apply(compiler) {
    +		let addedOnce = false;
    +		// Specify the event hook to attach to
    +		compiler.hooks.thisCompilation.tap(pluginName,
    +			(compilation) => compilation.hooks.processAssets.tap({
    +				name: pluginName,
    +				stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
    +			}, async (assets) => {
    +				if (addedOnce) return;
    +				addedOnce = true;
    +				console.log('[' + pluginName + '] Gathering Infos....');
    +
    +				// add static files from folder
    +				const rPath = path.resolve(__dirname, this.options.relpath);
    +				const sFiles = this.getAllFiles(rPath, '');
    +
    +				// Parallel compiling
    +				await Promise.all(sFiles.map((sFile) => {
    +					// finally return Promise of module compilation
    +					return new Promise((resolve) => {
    +						// only compile if wasm name match regex
    +						const sName = sFile.replace(/^.*[\\\/]/, '');
    +						if (!sName.endsWith('.' + this.options.extension)) {
    +							resolve(false);
    +							return;
    +						}
    +
    +						console.info(`[${pluginName}] Compile ${this.options.production ? 'production' : 'debug'}: ${sName}`);
    +
    +						// change new file ext to ".wasm"
    +						let newName = sName.replace(/\.[^/.]+$/, '');
    +						if (!newName.endsWith('.wasm')) newName += '.wasm';
    +
    +						this.compileWasm(rPath + sFile, newName, this.options.production)
    +							.then(async ({normal, map}) => {
    +							// emit files into compilation
    +								if (normal) await compilation.emitAsset(newName, new RawSource(normal));
    +								if (map) await compilation.emitAsset(newName + '.map', new RawSource(map));
    +
    +								console.info('[' + pluginName + '] Success: ' + newName);
    +								resolve(true);
    +							});
    +					});
    +				}));
    +
    +				// finalize
    +				if (this.options.cleanup) await this.cleanUp();
    +
    +				console.info('[' + pluginName + '] finished.');
    +			}),
    +		);
    +	}
    +
    +
    +	/**
    +		* @ignore
    +		* list files recursively
    +		* @param {strring} baseDir start directory
    +		* @param {string} subDir sub directory
    +		* @param {array} arrayOfFiles result files
    +		* @return {array} arrayOfFiles
    +		*/
    +	getAllFiles(baseDir, subDir, arrayOfFiles) {
    +		const sub = baseDir + '/' + subDir;
    +		const files = fs.readdirSync(sub);
    +		arrayOfFiles = arrayOfFiles || [];
    +		files.forEach((file) => {
    +			const fle = subDir + '/' + file;
    +			if (fs.statSync(sub + '/' + file).isDirectory()) {
    +				arrayOfFiles = this.getAllFiles(baseDir, fle, arrayOfFiles);
    +			} else {
    +				arrayOfFiles.push(fle);
    +			}
    +		});
    +		return arrayOfFiles;
    +	}
    +
    +
    +	/**
    +		* @ignore
    +		* compile assemblyscript (typescript) module to wasm and return binary
    +		* @param {string} inputPath module to compile
    +		* @param {strring} newName target name
    +		* @param {boolean} production create symbols/map ?
    +		* @return {Promise} finished binary(s)
    +		*/
    +	compileWasm(inputPath, newName, production) {
    +		return new Promise((resolve) => {
    +			try {
    +				const newOut = path.resolve(outPath, newName);
    +
    +				asc.main([
    +					inputPath,
    +					'--extension', this.options.extension,
    +					'--binaryFile', newOut,
    +					'--measure',
    +					'--runtime', 'full',
    +					production ? '--optimize' : '--sourceMap',
    +				], (err) => {
    +					// let output = execSync('npm run asbuild', { cwd: __dirname });
    +					if (err) throw err;
    +					// none? -> read and resolve optimized.wasm string
    +					resolve(production ? {
    +						normal: fs.readFileSync(newOut),
    +					} : {
    +						normal: fs.readFileSync(newOut),
    +						map: fs.readFileSync(newOut + '.map'),
    +					});
    +				});
    +			} catch (ex) {
    +				console.warn('[' + pluginName + '] Compile Error!');
    +				console.error(ex);
    +			}
    +		});
    +	}
    +
    +	/**
    +		* delete all files in the output dir
    +		* @return {Promise} async finished event
    +		*/
    +	cleanUp() {
    +		console.info('[' + pluginName + '] Cleaning...');
    +		return new Promise((resolve) => {
    +			fs.readdir(outPath, (err, files) => {
    +				if (err) throw err;
    +				Promise.all(files.map((file) => {
    +					return new Promise((res) => {
    +						fs.unlink(path.join(outPath, file), (err) => {
    +							if (err) throw err;
    +							console.info('[' + pluginName + '] delete: ' + file);
    +							res();
    +						});
    +					});
    +				})).then(resolve);
    +			});
    +		});
    +	}
    +}
    +
    +module.exports = WascBuilderPlugin;
    +
    +
    +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + diff --git a/docs/wasc-worker_WascInterface.ts.html b/docs/wasc-worker_WascInterface.ts.html new file mode 100644 index 0000000..5794ec9 --- /dev/null +++ b/docs/wasc-worker_WascInterface.ts.html @@ -0,0 +1,136 @@ + + + + + + + + + + wasc-worker/WascInterface.ts + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Source

    +

    wasc-worker/WascInterface.ts

    +
    + + + + + +
    +
    +
    /**
    + * @author hexxone / https://hexx.one
    + *
    + * @license
    + * Copyright (c) 2021 hexxone All rights reserved.
    + * Licensed under the GNU GENERAL PUBLIC LICENSE.
    + * See LICENSE file in the project root for full license information.
    + */
    +
    +import {ResultObject, ASUtil} from '@assemblyscript/loader';
    +
    +/**
    + * The shared interface for loading a module
    + */
    +export class WascInterface implements ResultObject {
    +    module: WebAssembly.Module;
    +    instance: WebAssembly.Instance;
    +    exports: ASUtil;
    +    run: (func, ...params) => Promise<any>;
    +}
    +
    +
    +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + diff --git a/docs/wasc-worker_WascLoader.ts.html b/docs/wasc-worker_WascLoader.ts.html new file mode 100644 index 0000000..f2c35d1 --- /dev/null +++ b/docs/wasc-worker_WascLoader.ts.html @@ -0,0 +1,292 @@ + + + + + + + + + + wasc-worker/WascLoader.ts + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Source

    +

    wasc-worker/WascLoader.ts

    +
    + + + + + +
    +
    +
    /**
    +* @author hexxone / https://hexx.one
    +*
    +* @license
    +* Copyright (c) 2021 hexxone All rights reserved.
    +* Licensed under the GNU GENERAL PUBLIC LICENSE.
    +* See LICENSE file in the project root for full license information.
    +*
    +* @description
    +* AssemblyScript module Loaders
    +*/
    +
    +import loader from '@assemblyscript/loader';
    +
    +import {makeRuntime, myFetch, ACTIONS, getTransferableParams, INITIAL_MEM} from './WascRT';
    +
    +import WascWorker from 'worker-loader!./Wasc';
    +import {WascInterface} from './WascInterface';
    +
    +/**
    +* Inline loads a compiled webassembly module.
    +* Basically the normal WebAssembly usage,
    +* just with api- and "run()"-compatibility
    +* @param {string} path compiled module path
    +* @param {number} initialMem initial memory size in kb
    +* @param {Object} options import Objects
    +* @return {Promise<WascInterface>} module
    +*/
    +export function LoadInline(path: string, initialMem: number = INITIAL_MEM, options: any = {}): Promise<WascInterface> {
    +	let ascExports: any;
    +	const memory = new WebAssembly.Memory({initial: initialMem});
    +	const staticImports = {
    +		env: {
    +			memory,
    +			logf(value) {
    +				console.log('F64: ' + value);
    +			},
    +			logi(value) {
    +				console.log('U32: ' + value);
    +			},
    +			logU32Array(ptr) {
    +				console.log(ascExports.getU32Array(ptr));
    +			},
    +			logF64Array(ptr) {
    +				console.log(ascExports.getF64Array(ptr));
    +			},
    +		},
    +	};
    +
    +	return new Promise(async (resolve) => {
    +		// get import object
    +		const {getImportObject} = options;
    +		const myImports = Object.assign({}, staticImports);
    +		if (getImportObject !== undefined) {
    +			Object.assign(myImports, getImportObject());
    +		}
    +
    +		const byteModule = await myFetch(path);
    +		const {module, instance, exports} = loader.instantiateSync(byteModule, myImports);
    +
    +		// get Exports
    +		const rtExports = makeRuntime(
    +			memory,
    +			exports.allocF64Array,
    +			exports.allocU32Array,
    +		);
    +
    +		// Add Helpers
    +		Object.assign(exports, {...rtExports});
    +		ascExports = exports;
    +
    +		/**
    +			* Run a function inside the worker.
    +			* @todo This is potentially dangerous due to eval!
    +			* @param {string} func stringified function to eval inside worker context
    +			* @param {Object} params Data to pass in
    +			* @return {Object} eval result
    +			*/
    +		function run(func, ...params) {
    +			return new Promise((res) => {
    +				const fun = new Function(`return ${func}`)();
    +				res(fun({
    +					module,
    +					instance,
    +					importObject: exports,
    +					params,
    +				}));
    +			});
    +		}
    +
    +		// we done here
    +		resolve({module, instance, exports, run} as any);
    +	});
    +}
    +
    +
    +/**
    +	* Creates a Worker, then loads a compiled webassembly module inside,
    +	* then creates a Promise-interface for all functions and additionally
    +	* wraps a "run" function inside the worker.
    +	* @param {string} source compiled module path
    +	* @param {Object} options import Objects
    +	* @return {Promise<WascInterface>} module
    +	*/
    +export function LoadWorker(source: string, options: any = {}): Promise<WascInterface> {
    +	// WRAP IN WORKER
    +	let currentId = 0;
    +	const promises = {};
    +	const worker = new WascWorker(options);
    +
    +	worker.onmessage = (e) => {
    +		const {id, result, action, payload} = e.data;
    +
    +		// COMPILE MODULE & RETURN EXPORTS
    +		if (action === ACTIONS.COMPILE_MODULE) {
    +			if (result === 0) {
    +				const {exports} = payload;
    +
    +				promises[id][0]({
    +
    +					// wrap the returned context/thread exports into promises
    +					exports: exports.reduce((acc, exp) => ({
    +						...acc,
    +						[exp]: (...params) => new Promise((...rest) => {
    +							promises[++currentId] = rest;
    +							worker.postMessage({
    +								id: currentId,
    +								action: ACTIONS.CALL_FUNCTION_EXPORT,
    +								payload: {
    +									func: exp,
    +									params,
    +								},
    +							}, getTransferableParams(params));
    +						}),
    +					}), {}),
    +
    +					// export context/thread run function
    +					run: (func, ...params) => new Promise((...rest) => {
    +						promises[++currentId] = rest;
    +						worker.postMessage({
    +							id: currentId,
    +							action: ACTIONS.RUN_FUNCTION,
    +							payload: {
    +								func: func.toString(),
    +								params,
    +							},
    +						}, getTransferableParams(params));
    +					}),
    +
    +				});
    +			} else if (result === 1) {
    +				promises[id][1](payload);
    +			}
    +
    +			// CALL FUNCTION
    +		} else if (
    +			action === ACTIONS.CALL_FUNCTION_EXPORT ||
    +				action === ACTIONS.RUN_FUNCTION
    +		) {
    +			promises[id][result](payload);
    +		}
    +
    +		promises[id] = null;
    +	};
    +
    +	return new Promise((...params) => {
    +		promises[++currentId] = [...params];
    +
    +		worker.postMessage({
    +			id: currentId,
    +			action: ACTIONS.COMPILE_MODULE,
    +			payload: source,
    +		});
    +	});
    +}
    +
    +
    +
    +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + diff --git a/docs/wasc-worker_WascRT.ts.html b/docs/wasc-worker_WascRT.ts.html new file mode 100644 index 0000000..4326354 --- /dev/null +++ b/docs/wasc-worker_WascRT.ts.html @@ -0,0 +1,277 @@ + + + + + + + + + + wasc-worker/WascRT.ts + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Source

    +

    wasc-worker/WascRT.ts

    +
    + + + + + +
    +
    +
    /**
    +*
    +* @author hexxone / https://hexx.one
    +*
    +* @license
    +* Copyright (c) 2021 hexxone All rights reserved.
    +* Licensed under the GNU GENERAL PUBLIC LICENSE.
    +* See LICENSE file in the project root for full license information.
    +* @ignore
    +*/
    +
    +// TODO customize this according to needs
    +export const INITIAL_MEM = 4096;
    +
    +/**
    +* @ignore
    +* Creates the required internal runtime functions
    +* @param {WebAssembly.Memory} memory initial WebAssembly memory
    +* @param {Function} allocF64
    +* @param {Function} allocU32
    +* @return {Object} Runtime
    +*/
    +export function makeRuntime(memory: WebAssembly.Memory, allocF64, allocU32) {
    +	let mem; let F64; let U32;
    +	const cached = new WeakMap();
    +
    +	/**
    +	* Refreshes the local memory-representation.
    +	* This is usually required, after new memory was allocated in WebAssembly
    +	*/
    +	function refreshMemory() {
    +		if (mem !== memory.buffer) {
    +			mem = memory.buffer;
    +			U32 = new Uint32Array(mem);
    +			F64 = new Float64Array(mem);
    +		}
    +	}
    +
    +	/**
    +	* Allocates a new Float64Array with given data in WebAssembly
    +	* @param {Array} typedArray Data to pass in
    +	* @return {Object} allocated array pointer
    +	*/
    +	function newF64Array(typedArray) {
    +		let ptr;
    +		if (!cached.has(typedArray)) {
    +			ptr = allocF64(typedArray.length);
    +			refreshMemory();
    +			cached.set(typedArray, ptr);
    +		} else {
    +			refreshMemory();
    +			ptr = cached.get(typedArray);
    +		}
    +		const dataStart = (U32[ptr >>> 2] >>> 2) + 2;
    +		F64.set(typedArray, dataStart >>> 1);
    +		return ptr;
    +	}
    +
    +	/**
    +	* Copies and returns the float64array at given pointer
    +	* @param {Object} ptr allocated array pointer
    +	* @return {Float64Array} data
    +	*/
    +	function getF64Array(ptr) {
    +		refreshMemory();
    +		ptr >>>= 2;
    +
    +		const offset = (U32[ptr] >>> 2) + 2;
    +		const len = U32[ptr + 1];
    +
    +		return F64.subarray(offset, offset + len);
    +	}
    +
    +	/**
    +	* Create a new Uint32Array in WebAssembly
    +	* @param {Array} typedArray Data to pass in
    +	* @return {Object} allocated array pointer
    +	*/
    +	function newU32Array(typedArray) {
    +		let ptr;
    +		if (!cached.has(typedArray)) {
    +			ptr = allocU32(typedArray.length);
    +			refreshMemory();
    +			cached.set(typedArray, ptr);
    +		} else {
    +			refreshMemory();
    +			ptr = cached.get(typedArray);
    +		}
    +		const dataStart = (U32[ptr >>> 2] >>> 2) + 2;
    +		U32.set(typedArray, dataStart);
    +		return ptr;
    +	}
    +
    +	/**
    +	* Copies and returns the uint32array at given pointer
    +	* @param {Object} ptr allocated array pointer
    +	* @return {Uint32Array} data
    +	*/
    +	function getU32Array(ptr) {
    +		refreshMemory();
    +		ptr >>>= 2;
    +
    +		const offset = (U32[ptr] >>> 2) + 2;
    +		const len = U32[ptr + 1];
    +
    +		return U32.subarray(offset, offset + len);
    +	}
    +
    +	return {
    +		newF64Array,
    +		getF64Array,
    +		newU32Array,
    +		getU32Array,
    +	};
    +}
    +
    +/**
    +* Small reusable fetch function, should work for local & web server files
    +* @param {string} path file to request
    +* @param {string} resType type of data to request (default = arraybuffer)
    +* @param {string} owMime force-override mime-type (optional)
    +* @return {Object} XMLHttpRequest.response (converted to resType)
    +*/
    +export function myFetch(path: string, resType: string = 'arraybuffer', owMime?: string): Promise<any> {
    +	return new Promise((res) => {
    +		const request = new XMLHttpRequest();
    +		request.open('GET', path);
    +		if (owMime) request.overrideMimeType(owMime);
    +		request.responseType = resType as any;
    +		request.onload = () => {
    +			if (request.status != 200) console.error(request);
    +			res(request.response);
    +		};
    +		request.send();
    +	});
    +}
    +
    +/**
    +* Worker <-> Maincontext communication
    +*/
    +/* eslint-disable no-unused-vars */
    +export enum ACTIONS {
    +	COMPILE_MODULE = 0,
    +	CALL_FUNCTION_EXPORT = 1,
    +	RUN_FUNCTION = 2
    +}
    +
    +/**
    +* Filter out parameters for passing them into WebWorkers
    +* @param {Object} params The given Items to filter
    +* @return {Object} WebWorker-passable items
    +*/
    +export function getTransferableParams(...params: any): any {
    +	return params.filter((x) => (
    +		(x instanceof ArrayBuffer) ||
    +		(x instanceof MessagePort) ||
    +		(x instanceof ImageBitmap)
    +	)) || [];
    +}
    +
    +
    +
    +
    +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + diff --git a/docs/wasc-worker_WascWorker.ts.html b/docs/wasc-worker_WascWorker.ts.html new file mode 100644 index 0000000..b1fcb33 --- /dev/null +++ b/docs/wasc-worker_WascWorker.ts.html @@ -0,0 +1,147 @@ + + + + + + + + + + wasc-worker/WascWorker.ts + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Source

    +

    wasc-worker/WascWorker.ts

    +
    + + + + + +
    +
    +
    /**
    + * @author Matteo Basso @https://github.com/mbasso
    + * @author hexxone / https://hexx.one
    + *
    + * @license
    + * Copyright (c) 2021 hexxone All rights reserved.
    + * Licensed under the GNU GENERAL PUBLIC LICENSE.
    + * See LICENSE file in the project root for full license information.
    + *
    +*/
    +
    +import {WascInterface} from './WascInterface';
    +import {LoadInline, LoadWorker} from './WascLoader';
    +
    +/**
    + * Initializes a new WebAssembly instance.
    + * @param {string} source compiled .wasm module path
    + * @param {Object} options passed to the module init
    + * @param {boolean} useWorker use worker or inline
    + * @return {Promise<WascInterface>} the initialized context
    + */
    +export default function WascWorker(source: string, options: any = {}, useWorker: boolean = true): Promise<WascInterface> {
    +	return new Promise(async (resolve) => {
    +		// initialize the actual module
    +		const promiseMe = (useWorker && Worker) ? LoadWorker : LoadInline;
    +		const result = await promiseMe(source, options);
    +
    +		// return the freshly initialized module
    +		resolve(result);
    +	});
    +}
    +
    +
    +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + diff --git a/docs/wasc-worker_index.ts.html b/docs/wasc-worker_index.ts.html new file mode 100644 index 0000000..8c12f2a --- /dev/null +++ b/docs/wasc-worker_index.ts.html @@ -0,0 +1,147 @@ + + + + + + + + + + wasc-worker/index.ts + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Source

    +

    wasc-worker/index.ts

    +
    + + + + + +
    +
    +
    /**
    + * @author Matteo Basso @https://github.com/mbasso
    + * @author hexxone / https://hexx.one
    + *
    + * @license
    + * Copyright (c) 2021 hexxone All rights reserved.
    + * Licensed under the GNU GENERAL PUBLIC LICENSE.
    + * See LICENSE file in the project root for full license information.
    + *
    +*/
    +
    +import {WascInterface} from './WascInterface';
    +import {LoadInline, LoadWorker} from './WascLoader';
    +
    +/**
    + * Initializes a new WebAssembly instance.
    + * @param {string} source compiled .wasm module path
    + * @param {Object} options passed to the module init
    + * @param {boolean} useWorker use worker or inline
    + * @return {Promise<WascInterface>} the initialized context
    + */
    +export default function WascInit(source: string, options: any = {}, useWorker: boolean = true): Promise<WascInterface> {
    +	return new Promise(async (resolve) => {
    +		// initialize the actual module
    +		const promiseMe = (useWorker && Worker) ? LoadWorker : LoadInline;
    +		const result = await promiseMe(source, options);
    +
    +		// return the freshly initialized module
    +		resolve(result);
    +	});
    +}
    +
    +
    +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + diff --git a/jsdoc.json b/jsdoc.json new file mode 100644 index 0000000..4c26639 --- /dev/null +++ b/jsdoc.json @@ -0,0 +1,25 @@ +{ + "opts": { + "template": "./../../node_modules/better-docs", + "encoding": "utf8", + "destination": "./docs/", + "recurse": true, + "readme": "./README.md", + "access": "all" + }, + "plugins": [ + "./../../node_modules/better-docs/typescript" + ], + "recurseDepth": 10, + "source": { + "include": ["./src"], + "exclude": ["./src/wasc-worker/node_modules"], + "includePattern": ".+\\.(jsx|js|tsx|ts)$", + "excludePattern": "(^|\\/|\\\\)_" + }, + "tags": { + "allowUnknownTags": true, + "dictionaries": ["jsdoc","closure"] + } +} + diff --git a/makedocs.bat b/makedocs.bat new file mode 100644 index 0000000..b7e2aeb --- /dev/null +++ b/makedocs.bat @@ -0,0 +1,3 @@ +@echo on + +jsdoc -c ./jsdoc.json \ No newline at end of file diff --git a/src/CComponent.ts b/src/CComponent.ts index 44b0979..c4577cd 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -1,53 +1,68 @@ /** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * represents a Core Component for Wallpaper Engine Wallpaper - * - */ +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ -import { CSettings } from "./CSettings"; -import { Smallog } from "./Smallog"; +import {CSettings} from './CSettings'; +import {Smallog} from './Smallog'; +/** +* Base-Component for a TypeScript Web Wallpaper +*/ export class CComponent { + private needsUpdate = false; - private needsUpdate = false; - - public settings: CSettings = null; - - // Important: Append your child objects, for settings to be applied correctly! - public children: CComponent[] = []; + /** main Settings, need to be overwritten with Specific settings + * @see {CSettings} + */ + public settings: CSettings = null; + /* Important: Append your child objects, for settings to be applied correctly! */ + public children: CComponent[] = []; - // will recursively try to set a setting with type and return success. - // will also flag the module as "needsUpdate" - public ApplySetting(key: any, value: any): boolean { - var found = this.settings.apply(key, value); - if (found) { - this.needsUpdate = true; - Smallog.Debug(`ApplySetting: ${key}:${value}`); - } - this.children.forEach(ch => found ||= ch.ApplySetting(key, value)); - return found; - } + /** + * will recursively try to set a setting with type and return success + *
    + * will also flag the module as needs-Update. + * + * @param {Object} key + * @param {Object} value + * @return {boolean} found + */ + public applySetting(key: any, value: any): boolean { + let found = this.settings.apply(key, value); + if (found) { + this.needsUpdate = true; + Smallog.debug(`ApplySetting: ${key}:${value}`); + } + this.children.forEach((ch) => found = (found || ch.applySetting(key, value))); + return found; + } - // will recursively update all needed modules afffter settings changes - // DO NOT OVERWWRITE !!! - public UpdateAll() { - this.children.forEach(c => c.UpdateAll()); - if (this.needsUpdate) this.UpdateSettings(); - this.needsUpdate = false; - } + /** + * DO NOT OVERWWRITE !!! + *
    + * will recursively update all needed modules after settings changes + */ + public updateAll() { + this.children.forEach((c) => c.updateAll()); + if (this.needsUpdate) this.updateSettings(); + this.needsUpdate = false; + } - // NEEDS TO BE OVERWRITTEN FOR DOING ACTIONS ON SETTINGS CHANGE - // should usually get called automatically when needed.. no need for extra calling - public UpdateSettings(): Promise { - console.error(`ERROR_NO_IMPL at: CComponent.UpdateSettings!\r\nPlease override this method!`) - return; - } -} \ No newline at end of file + /** + * NEEDS TO BE OVERWRITTEN FOR DOING ACTIONS ON SETTINGS CHANGE. + *
    + * should usually get called automatically when needed.. no need for extra calling + * + * @return {Promise} async commpletion event + */ + public updateSettings(): Promise { + console.error(`ERROR_NO_IMPL at: CComponent.UpdateSettings!\r\nPlease override this method!`); + return Promise.resolve(); + } +} diff --git a/src/CSettings.ts b/src/CSettings.ts index 06a47b7..760198c 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -1,32 +1,40 @@ /** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * Core Settings interface, used for type-secure setting applying. - * - * All Settings-classes should be dereived from this one. - * - */ +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ -import { Smallog } from "./Smallog"; +import {Smallog} from './Smallog'; +/** +* Base-Component Settings helper +* +* Core Settings interface, used for type-secure setting applying. +* +* All Settings-classes should be dereived from this one. +* +* @see {CComponent} +*/ export class CSettings { - - // check if a certain key exists on a (dereived) object and the value type matches - public apply(key: string, castedValue: any) { - if (this[key] !== undefined) { - if (typeof this[key] === typeof castedValue) { - this[key] = castedValue; - return true; - } - else Smallog.Error("CSettings Error: invalid type on: '" + key + - "'. Is: '" + typeof this[key] + "', applied: '" + typeof castedValue + "'"); - } - return false; - } + /** + * check if a certain key exists on a (dereived) object and the value type matches + * @param {string} key + * @param {Object} castedValue + * @return {boolean} success + */ + public apply(key: string, castedValue: any) { + if (this[key] !== undefined) { + if (typeof this[key] === typeof castedValue) { + this[key] = castedValue; + return true; + } else { + Smallog.Error('CSettings Error: invalid type on: \'' + key + + '\'. Is: \'' + typeof this[key] + '\', applied: \'' + typeof castedValue + '\''); + } + } + return false; + } } diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index 812c126..d80be51 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -1,107 +1,128 @@ /** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * Displays a html reload bar for a given Time. - * - */ +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +* +* @description +* Displays a html reload bar for a given Time. +* +*/ -import { CComponent } from "./CComponent"; -import { CSettings } from "./CSettings"; -import { Ready } from "./Util"; +import {CComponent} from './CComponent'; +import {CSettings} from './CSettings'; +import {waitReady} from './Util'; +/** + * Reload-bar settings + */ class ReloadSettings extends CSettings { - reload_seconds: number = 3; + reload_seconds: number = 3; } +/** + * Visual Reload-Bar + */ export class ReloadHelper extends CComponent { + public settings: ReloadSettings = new ReloadSettings(); - public settings: ReloadSettings = new ReloadSettings(); - - constructor() { - super(); - Ready().then(() => { - this.injectCSS(); - this.injectHTML(); - }); - } + /** + * Create and prepare when document is ready + */ + constructor() { + super(); + waitReady().then(() => { + this.injectCSS(); + this.injectHTML(); + }); + } - private injectCSS() { - var st = document.createElement("style"); - st.innerHTML = ` - #reload-bar { - position: absolute; - opacity: 0; - top: 0px; - height: 10px; - width: 0%; - background-color: #989a; - } - #reload-bar.show { - opacity: 1; - width: 100%; - background-color: #e11a; - transition: all ${this.settings.reload_seconds}s ease, opacity 0.33s ease; - } - #reload-bar.done { - transition: opacity 0.33s ease; - } - #reload-text { - position: absolute; - top: -6em; - width: 100%; - text-align: center; - font-weight: 100; - font-size: 3em; - color: #fffa; - } - #reload-text.show { - top: 10px; - color: #e11a; - text-shadow: 0 0 20px rgba(255, 50, 50, .5), 0 0 15px rgba(255, 50, 50, .5); - transition: all .33s ease, color ${this.settings.reload_seconds}s ease, text-shadow ${this.settings.reload_seconds}s ease; - } - #reload-text.done { - transition: position 0.33s linear; - } - #reload-text { - text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5); - } - `; - document.head.append(st); - } + /** + * Make custom style + */ + private injectCSS() { + const st = document.createElement('style'); + st.innerHTML = ` + #reload-bar { + position: absolute; + opacity: 0; + top: 0px; + height: 10px; + width: 0%; + background-color: #989a; + } + #reload-bar.show { + opacity: 1; + width: 100%; + background-color: #e11a; + transition: all ${this.settings.reload_seconds}s ease, opacity 0.33s ease; + } + #reload-bar.done { + transition: opacity 0.33s ease; + } + #reload-text { + position: absolute; + top: -6em; + width: 100%; + text-align: center; + font-weight: 100; + font-size: 3em; + color: #fffa; + } + #reload-text.show { + top: 10px; + color: #e11a; + text-shadow: 0 0 20px rgba(255, 50, 50, .5), 0 0 15px rgba(255, 50, 50, .5); + transition: all .33s ease, color ${this.settings.reload_seconds}s ease, text-shadow ${this.settings.reload_seconds}s ease; + } + #reload-text.done { + transition: position 0.33s linear; + } + #reload-text { + text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5); + } + `; + document.head.append(st); + } - private injectHTML() { - var outer = document.createElement("div"); - outer.id = "reloadhelper"; - var bar = document.createElement("div"); - bar.id = "reload-bar"; - var tex = document.createElement("h1"); - tex.id = "reload-text"; - tex.innerHTML = "Reload"; - outer.append(bar, tex); - document.body.append(outer); - } + /** + * Make custom html elements + */ + private injectHTML() { + const outer = document.createElement('div'); + outer.id = 'reloadhelper'; + const bar = document.createElement('div'); + bar.id = 'reload-bar'; + const tex = document.createElement('h1'); + tex.id = 'reload-text'; + tex.innerHTML = 'Reload'; + outer.append(bar, tex); + document.body.append(outer); + } - // @Todo test: bar always reset to 0 on show ?? - public Show(visible: boolean) { - const e1 = document.getElementById("reload-bar"); - const e2 = document.getElementById("reload-text"); - if(visible) { - e1.classList.add("show"); - e2.classList.add("show"); - } - else { - e1.classList.remove("show"); - e2.classList.remove("show"); - } - } + /** + * @Todo test: bar always reset to 0 on show ?? + * @param {boolean} visible + */ + public show(visible: boolean) { + const e1 = document.getElementById('reload-bar'); + const e2 = document.getElementById('reload-text'); + if (visible) { + e1.classList.add('show'); + e2.classList.add('show'); + } else { + e1.classList.remove('show'); + e2.classList.remove('show'); + } + } - // dont print IMPL error - public UpdateSettings(): Promise { return; } -}; \ No newline at end of file + /** + * dont print IMPL error + * @return {Promise} + */ + public updateSettings(): Promise { + return Promise.resolve(); + } +}; diff --git a/src/Smallog.ts b/src/Smallog.ts index 5a19092..4008e6b 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -1,63 +1,73 @@ + /** - * @author D.Thiele @https://hexx.one - */ - - -export function TraceCall(def: string, depth: number = 3) { - try { - throw new Error("TraceCall()"); - } - catch (e) { - // Examine e.stack here - if (e.stack) { - const splt = e.stack.split(/\n/); - if (splt.length > depth) return "[" + splt[depth].trim().substring(3) + "] "; - } - } - return def; +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +/* eslint-disable no-unused-vars */ +/* eslint-disable require-jsdoc */ + +export function traceCall(def: string, depth: number = 3) { + try { + throw new Error('TraceCall()'); + } catch (e) { + // Examine e.stack here + if (e.stack) { + const splt = e.stack.split(/\n/); + if (splt.length > depth) return '[' + splt[depth].trim().substring(3) + '] '; + } + } + return def; } export enum LogLevel { - Error = 0, - Info = 1, - Debug = 2 + Error = 0, + Info = 1, + Debug = 2 } export module Smallog { - var logLevel: LogLevel = LogLevel.Debug; // todo level Info for release + let logLevel: LogLevel = LogLevel.Debug; // todo level Info for release + let preFix: string = '[Smallog] '; + let printTime: boolean = false; - var preFix: string = "[Smallog] "; - var printTime: boolean = false; + export function GetLevel() { + return logLevel; + } - export function GetLevel() { - return logLevel; - } + export function setLevel(level: LogLevel) { + logLevel = level; + } - export function SetLevel(level: LogLevel) { - logLevel = level; - } + export function setPrefix(pre: string) { + preFix = pre; + } - export function SetPrefix(pre: string) { - preFix = pre; - } + export function SetPrintTime(print: boolean) { + printTime = print; + } - export function Error(msg: string, hdr: string = preFix) { - Log(console.error, msg, TraceCall(hdr)); - } + export function Error(msg: string, hdr: string = preFix) { + log(console.error, msg, traceCall(hdr)); + } - export function Info(msg: string, hdr: string = preFix) { - if (logLevel >= 2) hdr = TraceCall(hdr); - if (logLevel >= 1) Log(console.info, msg, hdr); - } + export function info(msg: string, hdr: string = preFix) { + if (logLevel >= 2) hdr = traceCall(hdr); + if (logLevel >= 1) log(console.info, msg, hdr); + } - export function Debug(msg: string, hdr: string = preFix) { - if (logLevel >= 2) Log(console.debug, msg, TraceCall(hdr)); - } + export function debug(msg: string, hdr: string = preFix) { + if (logLevel >= 2) log(console.debug, msg, traceCall(hdr)); + } - function Log(call: any, msg: string, hdr: string) { - var m = msg; - if (printTime) m = ("[" + new Date().toLocaleString() + "] ") + m; - call(hdr + m); - } -} \ No newline at end of file + function log(call: any, msg: string, hdr: string) { + let m = msg; + if (printTime) m = ('[' + new Date().toLocaleString() + '] ') + m; + call(hdr + m); + } +} diff --git a/src/Stats.ts b/src/Stats.ts index fe1db4f..d0501f5 100644 --- a/src/Stats.ts +++ b/src/Stats.ts @@ -1,12 +1,15 @@ /** - * @author D.Thiele @https://hexx.one - * - * @description - * TypeScript Wrapper for mrdoob Stats.js - * Still requires Stats.js to be included! - * - */ - +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +* +* TypeScript Wrapper for mrdoob Stats.js +* Still requires Stats.js to be included! +* @ignore +*/ declare interface Stats { REVISION: number; @@ -28,7 +31,8 @@ declare namespace Stats { update(value: number, maxValue: number): void; } + // eslint-disable-next-line no-unused-vars function Panel(name?: string, fg?: string, bg?: string): Panel; } -export default Stats; \ No newline at end of file +export default Stats; diff --git a/src/Util.ts b/src/Util.ts index 7fb26ee..1e4dacf 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -1,26 +1,26 @@ /** - * @author D.Thiele - * @url https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * Shorthand Document ready wrapper - */ +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +* +* @description +* Shorthand Document ready wrapper +*/ -export function Ready() { - return new Promise(resolve => { - // If document is already loaded, run method - if (document.readyState === 'interactive' || document.readyState === 'complete') - resolve(true); - // Otherwise, wait until document is loaded - document.addEventListener('DOMContentLoaded', resolve, false); - }); -} - -export function ToggleClass(id: string, clas: string) { - -} \ No newline at end of file +/** +* Helper function +* @return {Promise} resolve when site ready +*/ +export function waitReady() { + return new Promise((resolve) => { + // If document is already loaded, run method + if (document.readyState === 'interactive' || document.readyState === 'complete') { + resolve(true); + } + // Otherwise, wait until document is loaded + document.addEventListener('DOMContentLoaded', resolve, false); + }); +} diff --git a/src/WEAS.ts b/src/WEAS.ts new file mode 100644 index 0000000..d8c705c --- /dev/null +++ b/src/WEAS.ts @@ -0,0 +1,261 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +import {ASUtil} from '@assemblyscript/loader'; +import {CComponent} from './CComponent'; +import {CSettings} from './CSettings'; +import {waitReady} from './Util'; +import {Smallog} from './Smallog'; + +import wascWorker from './wasc-worker/WascWorker'; +import {WascInterface} from './wasc-worker/WascInterface'; + +const DAT_LEN = 128; + +/** +* Audio processing settings +* @extends {CSettings} +*/ +export class WEASettings extends CSettings { + /** do audio processing? */ + public audioprocessing: boolean = true; + // do pink-noise processing? + public equalize: boolean = true; + // convert to mono? + public mono_audio: boolean = true; + // invert low & high freqs? + public audio_direction: number = 0; + // peak filtering + public peak_filter: number = 1; + // neighbour-smoothing value + public value_smoothing: number = 2; + // time-value smoothing ratio + public audio_increase: number = 75; + public audio_decrease: number = 35; + // multipliers + public treble_multiplier: number = 0.5; + public mids_multiplier: number = 0.75; + public bass_multiplier: number = 1.8; + // ignore value leveling for "silent" data + public minimum_volume: number = 0.005; + // use low latency audio? + public low_latency: boolean = false; +} + +const SettIDs = { + equalize: 0, + mono_audio: 1, + audio_direction: 2, + peak_filter: 3, + value_smoothing: 4, + audio_increase: 5, + audio_decrease: 6, + treble_multiplier: 7, + mids_multiplier: 8, + bass_multiplier: 9, + minimum_volume: 10, +}; + +const PropIDs = { + bass: 0, + mids: 1, + highs: 2, + sum: 3, + min: 4, + max: 5, + average: 6, + range: 7, + silent: 8, + intensity: 9, +}; + +/** +* WEAS +*
    +* Wallpaper Engine Audio Supplier makes working with audio easier. +*
    +* It will automatically start to receive and process the audio data +* which can then be accessed on the global object. +*
    +* DEPENDS ON: +*
    +* - Wallpaper Engine Web Wallpaper environment +*
    +* - audio-processing supported web wallpaper +* @extends {CComponent} +*/ +export class WEAS extends CComponent { + // last processed audio object + public lastAudio = null; + + // settings object + public settings: WEASettings = new WEASettings(); + + // create transfer buffer + private inBuff = new Float64Array(DAT_LEN); + + // web assembly functions + private weasModule: WascInterface = null; + + /** + * delay audio initialization until page ready + */ + constructor() { + super(); + waitReady().then(() => this.realInit()); + } + + /** + * initializes audio processing pipeline + * and starts listening on audio data + */ + private async realInit() { + // if wallpaper engine context given, listen + if (!window['wallpaperRegisterAudioListener']) { + Smallog.info('\'window.wallpaperRegisterAudioListener\' not given!'); + return; + } + + this.weasModule = await wascWorker('WEAS.wasm', {}, !this.settings.low_latency); + const {run} = this.weasModule; + + // pass settings to module + await this.updateSettings(); + + const self = this; + + // register audio callback on module + window['wallpaperRegisterAudioListener']((audioArray) => { + // Smallog.debug('Get Audio Data!'); + // check basic + if (!self.settings.audioprocessing || audioArray == null || audioArray.length != DAT_LEN) { + Smallog.Error('audioListener: received invalid audio data array: ' + JSON.stringify([audioArray.length || null, audioArray])); + return; + } + // check nulls + let consecutiveNull = 0; + for (let i = 0; i < 15; i++) { + const aA = audioArray[Math.floor(Math.random() * audioArray.length)]; + if (aA == 0.0) consecutiveNull++; + else consecutiveNull = 0; + if (consecutiveNull > 10) { + Smallog.debug('Skipping received Null data!: ' + JSON.stringify(audioArray)); + return; + } + } + + // prepare data + const start = performance.now(); + self.inBuff.set(audioArray); + + // WRAP IN isolated Function ran inside worker + run(({module, instance, importObject, params}) => { + const {exports} = instance; + const {data} = params[0]; + const io = importObject as ASUtil; + + // set audio data directly in module memory + io.__getFloat64ArrayView(exports.inputData).set(data); + // trigger processing processing + exports.update(); + // get copy of updated Data & Properties + const r = { // => result + data: new Float64Array(io.__getFloat64ArrayView(exports.outputData)), + props: new Float64Array(io.__getFloat64ArrayView(exports.audioProps)), + }; + return r; + }, // params passed to worker + { + data: self.inBuff, + }) + // worker result, back in main context + .then((result) => { + const {data, props} = result; + const realProps = self.getProps(props); + // apply actual last Data from worker + self.lastAudio = { + time: start / 1000, + data, + ...realProps, + }; + // print info + Smallog.debug('Got Data from Worker! Time= ' + (performance.now() - start) + ', Data= ' + JSON.stringify(realProps)); + }); + }); + } + + /** + * converts calculated output property number-array to string-associative-array + * @param {ArrayLike} dProps processed properties + * @return {Object} + */ + private getProps(dProps: ArrayLike) { + const keys = Object.keys(PropIDs); + const res = {}; + for (let index = 0; index < keys.length; index++) { + const key = keys[index]; + res[key] = dProps[PropIDs[key]]; + } + return res; + } + + /** + * !! CAVEAT: only available after init and module load !! + *
    + * Will send the processing settings to the WebAssembly module + * @return {Promise} finished event + */ + public updateSettings(): Promise { + if (!this.weasModule) return; + const {run} = this.weasModule; + + return new Promise((resolve) => { + const keys = Object.keys(SettIDs); + const sett = new Float64Array(keys.length); + for (let index = 0; index < keys.length; index++) { + const key = keys[index]; + sett[SettIDs[key]] = this.settings[key] || 0; + } + + // isolated Function running inside worker + run(({module, instance, importObject, params}) => { + const {exports} = instance; + const {data} = params[0]; + const io = importObject as ASUtil; + io.__getFloat64ArrayView(exports.audioSettings).set(data); + }, + // Data passed to worker + { + data: sett, + }) + // Back to main context + .then(() => { + Smallog.info('Sent Settings to WEAS: ' + JSON.stringify(sett)); + resolve(); + }); + }); + } + + /** + * @return {boolean} false if: + *
    + * - processing is disabled + *
    + * - there is no data + *
    + * - the data is silent + *
    + * - data is too old (> 3s) + */ + public hasAudio() { + return this.settings.audioprocessing && + this.lastAudio && this.lastAudio.silent == 0 && + (performance.now() / 1000 - this.lastAudio.time < 3); + } +} diff --git a/src/weas/WEAS.wasm.asc b/src/WEAS.wasm.asc similarity index 85% rename from src/weas/WEAS.wasm.asc rename to src/WEAS.wasm.asc index dc5212f..7a1bcac 100644 --- a/src/weas/WEAS.wasm.asc +++ b/src/WEAS.wasm.asc @@ -1,8 +1,8 @@ /** - * @author D.Thiele @https://hexx.one + * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2020 D.Thiele All rights reserved. + * Copyright (c) 2021 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * @@ -180,70 +180,81 @@ function isOn(a: f64): boolean { } -// THEW INPUT VALUE TO WRITE TO +// INPUT AUDIO DATA export const inputData = new Float64Array(DAT_LEN); -// this will hold the current processed audio data +// working data +const workData = new Float64Array(DAT_LEN); + +// PROCESSED AUDIO DATA // either: B-H | H-B | HL-BL-BR-HR | BL-HL-HR-BR // where ( B=bass, H=high, L=left, R=right ) // in range > 0.0 and ~< 1.5 export const outputData = new Float64Array(DAT_LEN); -// this will hold the current processed properties: +// processed properties: export const audioProps = new Float64Array(10); -// this will hold the current processing settings +// processing settings export const audioSettings = new Float64Array(11); // this will update and process new data export function update(): void { + // copy the input data to a working buffer + // so the input could be updated in the meantime + workData.set(inputData); + // fix pink noise? if (isOn(audioSettings[0])) - correctPinkNoise(inputData); + correctPinkNoise(workData); // write botch channels to mono if (isOn(audioSettings[1])) - stereoToMono(inputData); + stereoToMono(workData); if (isOn(audioSettings[2])) { // flipped high & low mapping if (isOn(audioSettings[1])) // flip whole range - invertAll(inputData); + invertAll(workData); else { // only flip first half of stereo - invertFirst(inputData); + invertFirst(workData); } } else { // normal high & low mapping if (isOn(audioSettings[1])) { // only flip the second half of the data - invertSecond(inputData); + invertSecond(workData); } } // process peaks? if (isOn(audioSettings[3])) - peakFilter(inputData, audioSettings[3] + 1); + peakFilter(workData, audioSettings[3] + 1); // smooth data? if (isOn(audioSettings[4])) - smoothArray(inputData, Math.floor(audioSettings[4]) as i32); + smoothArray(workData, Math.floor(audioSettings[4]) as i32); // process with last data - applyValueLeveling(inputData, outputData, + applyValueLeveling(workData, outputData, audioSettings[5], audioSettings[6]); // process current frequency data and previous - sum = max = bass = mids = peaks = 0; + sum = 0; + max = 0; + bass = 0; + mids = 0; + peaks = 0; min = 1; for (indx = 0; indx < DAT_LEN; indx++) { // parse current freq value - tmpF = inputData[indx]; + tmpF = workData[indx]; // process min max value if (tmpF < min) min = tmpF; if (tmpF > max) max = tmpF; @@ -257,12 +268,22 @@ export function update(): void { // calc average with previous entry average = sum / (DAT_LEN as f64); - silent = (max < audioSettings[10] / 1000) ? 0.9999 : 0.00; - intensity = (bass * 8 - mids + peaks) / 6 / average; + silent = (max < audioSettings[10] / 1000) ? 1 : 0; + intensity = (bass * 8 - mids + peaks) / 6 / (average + 0.01); range = max - min; // Apply Data - outputData.set(inputData); - audioProps.set([bass, mids, peaks, sum, min, max, average, range, silent, intensity]) + outputData.set(workData); + audioProps[0] = bass; + audioProps[1] = mids; + audioProps[2] = peaks; + audioProps[3] = sum; + audioProps[4] = min; + audioProps[5] = max; + audioProps[6] = average; + audioProps[7] = range; + audioProps[8] = silent; + audioProps[9] = intensity; + // DONE } diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 418b7ba..7707a5b 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -1,510 +1,551 @@ /** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @see - * REQUIRES: - * - jQuery - * - * @description - * WEICUE - * Wallpaper Engine iCUE effects for web wallpapers - * - * Uses several different methods to create - * Lighting effects for Corsair ICUE devices. - */ +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ -import { CComponent } from "./CComponent"; -import { CSettings } from "./CSettings"; -import { Ready } from "./Util"; -import { Smallog } from "./Smallog"; -import { WEAS } from "./weas/WEAS"; +import {CComponent} from './CComponent'; +import {CSettings} from './CSettings'; +import {waitReady} from './Util'; +import {Smallog} from './Smallog'; +import {WEAS} from './WEAS'; -const ClassName: string = "[WEICUE] "; +const ClassName: string = '[WEICUE] '; const canvasX: number = 23; const canvasY: number = 7; const WaitTime: number = 30; const Transition: number = 3; +/** +* iCUE processing settings +* @extends {CSettings} +*/ export class CUESettings extends CSettings { - icue_mode: number = 1; - icue_area_xoff: number = 50; - icue_area_yoff: number = 90; - icue_area_width: number = 75; - icue_area_height: number = 30; - icue_area_blur: number = 5; - icue_area_decay: number = 15; - icue_area_preview: boolean = false; - icue_main_color: string = "0 0.8 0"; - // AudiOrbits bg Color; used as "decay"-color aswell - main_color: string = "0 0 0"; + public icue_mode: number = 1; + public icue_area_xoff: number = 50; + public icue_area_yoff: number = 90; + public icue_area_width: number = 75; + public icue_area_height: number = 30; + public icue_area_blur: number = 5; + public icue_area_decay: number = 15; + public icue_area_preview: boolean = false; + public icue_main_color: string = '0 0.8 0'; + // AudiOrbits bg Color; used as "decay"-color aswell + public main_color: string = '0 0 0'; } +/** +* WEICUE +*
    +* Wallpaper Engine iCUE effects for web wallpapers +*
    +* Uses several different methods to create +* Lighting effects for Corsair ICUE devices. +* @extends {CComponent} +*/ export class WEICUE extends CComponent { + private weas: WEAS = null; - private weas: WEAS = null; - - private holder: HTMLDivElement = null; - private texter: HTMLDivElement = null; - private preview: HTMLDivElement = null; - private helperCanvas: HTMLCanvasElement = null; - private helperContext: CanvasRenderingContext2D = null; + private holder: HTMLDivElement = null; + private texter: HTMLDivElement = null; + private preview: HTMLDivElement = null; + private helperCanvas: HTMLCanvasElement = null; + private helperContext: CanvasRenderingContext2D = null; - private icueDevices = []; - private icueInterval = null; + private icueDevices = []; + private icueInterval = null; - // runtime values - public settings: CUESettings = new CUESettings(); + // runtime values + public settings: CUESettings = new CUESettings(); - public isAvailable: boolean = false; - public PAUSED: boolean = false; + public isAvailable: boolean = false; + public PAUSED: boolean = false; - constructor(weas: WEAS) { - super(); - this.weas = weas; + /** + * Starts listening for led/icue plugin + * and prepares helper elements + * @param {WEAS} weas Audio supplier for non-projection mode + */ + constructor(weas: WEAS) { + super(); + this.weas = weas; - // Plugin handler - window['wallpaperPluginListener'] = { - onPluginLoaded: function (name, version) { - Smallog.Info("Plugin: " + name + ", Version: " + version + " : registered.", ClassName); - if (name === 'cue') this.isAvailable = true; - if (name === 'led') this.isAvailable = true; - } - } - Ready().then(() => { - // inject helpers - this.injectCSS(); - this.injectHTML(); - this.init(); - }); - } + // Plugin handler + window['wallpaperPluginListener'] = { + onPluginLoaded: function(name, version) { + Smallog.info('Plugin: ' + name + ', Version: ' + version + ' : registered.', ClassName); + if (name === 'cue') this.isAvailable = true; + if (name === 'led') this.isAvailable = true; + }, + }; + waitReady().then(() => { + // inject helpers + this.injectCSS(); + this.injectHTML(); + this.init(); + }); + } - private injectCSS() { - var st = document.createElement("style"); - st.innerHTML = ` - #icueholder { - opacity: 0; - position: absolute; - top: -120px; - left: 0; - width: auto; - height: auto; - margin: 10px; - transition: all ${Transition}s ease; - } - #icueholder.show { - opacity: 1; - top: 0px; - } - #icueholder.waiting { - opacity: 0.2; - transition: all 10s ease; - } - #icuelogo { - float: left; - height: 80px; - width: 80px; - } - #icuetext { - float: left; - margin: 25px 5px; - font-size: 175%; - } - #icueholder { - text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5); - } - .cuePreview { - position: absolute; - background: rgba(255, 0, 0, .3); - } - `; - document.head.append(st); - } + /** + * style for iCue messages, preview and helper + */ + private injectCSS() { + const st = document.createElement('style'); + st.innerHTML = ` + #icueholder { + opacity: 0; + position: absolute; + top: -120px; + left: 0; + width: auto; + height: auto; + margin: 10px; + transition: all ${Transition}s ease; + } + #icueholder.show { + opacity: 1; + top: 0px; + } + #icueholder.waiting { + opacity: 0.2; + transition: all 10s ease; + } + #icuelogo { + float: left; + height: 80px; + width: 80px; + } + #icuetext { + float: left; + margin: 25px 5px; + font-size: 175%; + } + #icueholder { + text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5); + } + .cuePreview { + position: absolute; + background: rgba(255, 0, 0, .3); + } + `; + document.head.append(st); + } - private injectHTML() { - // create container - this.holder = document.createElement("div"); - this.holder.id = "icueholder"; - // create icon (no ref needed) - var imgg = document.createElement("img"); - imgg.id = "icuelogo"; - // @TODO remove this abomination - imgg.setAttribute("src", ` -data:image/png;base64,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 -AAAASUVORK5CYII= - `); - // make text holder - this.texter = document.createElement("div"); - this.texter.id = "icuetext"; - // append image and text - this.holder.append(imgg, this.texter); - document.body.append(this.holder); - } + /** + * Prepare html elements + */ + private injectHTML() { + // create container + this.holder = document.createElement('div'); + this.holder.id = 'icueholder'; + // create icon (no ref needed) + const imgg = document.createElement('img'); + imgg.id = 'icuelogo'; + // @TODO remove this abomination + imgg.setAttribute('src', ` + data:image/png;base64,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 + AAAASUVORK5CYII= + `); + // make text holder + this.texter = document.createElement('div'); + this.texter.id = 'icuetext'; + // append image and text + this.holder.append(imgg, this.texter); + document.body.append(this.holder); + } - // show a message by icue - private icueMessage(msg, waiting :boolean = false) { - Smallog.Debug("MSG: " + msg, ClassName); - // set text - this.texter.innerHTML = msg; - // show - this.holder.classList.add("show"); - if(waiting) this.holder.classList.add("waiting"); - // hide again - const waiTime = waiting ? (WaitTime) : (Transition * 1000 + 4000); - setTimeout(() => { - this.holder.classList.remove("show"); - if(waiting) this.holder.classList.remove("waiting"); - }, waiTime); - } + /** + * show a message by icue + * @param {string} msg + * @param {boolean} waiting + */ + private icueMessage(msg: string, waiting :boolean = false) { + Smallog.debug('MSG: ' + msg, ClassName); + // set text + this.texter.innerHTML = msg; + // show + this.holder.classList.add('show'); + if (waiting) this.holder.classList.add('waiting'); + // hide again + const waiTime = waiting ? (WaitTime) : (Transition * 1000 + 4000); + setTimeout(() => { + this.holder.classList.remove('show'); + if (waiting) this.holder.classList.remove('waiting'); + }, waiTime); + } - // helper - private getArea(inPx?) { - var sett = this.settings; - var wwid = window.innerWidth; - var whei = window.innerHeight; - var w = wwid * sett.icue_area_width / 100; - var h = whei * sett.icue_area_height / 100; - var l = ((wwid - w) * sett.icue_area_xoff / 100); - var t = ((whei - h) * sett.icue_area_yoff / 100); - return { - width: w + (inPx ? "px" : ""), - height: h + (inPx ? "px" : ""), - left: l + (inPx ? "px" : ""), - top: t + (inPx ? "px" : ""), - } - } + /** + * helper + * @param {boolean} inPx suffix "px" string to number (allows direct css use) + * @return {Object} area + */ + private getArea(inPx = false) { + const sett = this.settings; + const wwid = window.innerWidth; + const whei = window.innerHeight; + const w = wwid * sett.icue_area_width / 100; + const h = whei * sett.icue_area_height / 100; + const l = ((wwid - w) * sett.icue_area_xoff / 100); + const t = ((whei - h) * sett.icue_area_yoff / 100); + return { + width: w + (inPx ? 'px' : ''), + height: h + (inPx ? 'px' : ''), + left: l + (inPx ? 'px' : ''), + top: t + (inPx ? 'px' : ''), + }; + } - // get data for icue - private getEncodedCanvasImageData(imageData) { - var colorArray = []; - for (var d = 0; d < imageData.data.length; d += 4) { - var write = d / 4 * 3; - colorArray[write] = imageData.data[d]; - colorArray[write + 1] = imageData.data[d + 1]; - colorArray[write + 2] = imageData.data[d + 2]; - } - return String.fromCharCode.apply(null, colorArray); - } + /** + * convert data for icue + * @param {ImageData} imageData + * @return {string} + */ + private getEncodedCanvasImageData(imageData: ImageData) { + const colorArray = []; + for (let d = 0; d < imageData.data.length; d += 4) { + const write = d / 4 * 3; + colorArray[write] = imageData.data[d]; + colorArray[write + 1] = imageData.data[d + 1]; + colorArray[write + 2] = imageData.data[d + 2]; + } + return String.fromCharCode.apply(null, colorArray); + } - // canvas blur helper function - private gBlurCanvas(canvas, ctx, blur) { - var sum = 0; - var delta = 5; - var alpha_left = 1 / (2 * Math.PI * delta * delta); - var step = blur < 3 ? 1 : 2; + /** + * canvas blur helper function + * @param {HTMLCanvasElement} canvas + * @param {CanvasRenderingContext2D} ctx + * @param {number} blur + */ + private gBlurCanvas(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, blur: number) { + let sum = 0; + const delta = 5; + const alpha_left = 1 / (2 * Math.PI * delta * delta); + const step = blur < 3 ? 1 : 2; - var x, weight; - for (var y = -blur; y <= blur; y += step) { - for (x = -blur; x <= blur; x += step) { - weight = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta)); - sum += weight; - } - } - for (var y = -blur; y <= blur; y += step) { - for (x = -blur; x <= blur; x += step) { - ctx.globalAlpha = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta)) / sum * blur * blur; - ctx.drawImage(canvas, x, y); - } - } - ctx.globalAlpha = 1; - } + let x; let weight; + for (let y = -blur; y <= blur; y += step) { + for (x = -blur; x <= blur; x += step) { + weight = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta)); + sum += weight; + } + } + for (let y = -blur; y <= blur; y += step) { + for (x = -blur; x <= blur; x += step) { + ctx.globalAlpha = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta)) / sum * blur * blur; + ctx.drawImage(canvas, x, y); + } + } + ctx.globalAlpha = 1; + } - private init() { - var sett = this.settings; - // dont initialize if disabled - if (sett.icue_mode == 0) return; + /** + * Show waiting message and init canvas + */ + private init() { + const sett = this.settings; + // dont initialize if disabled + if (sett.icue_mode == 0) return; - this.icueMessage("LED: waiting for plugin.", true); - this.initCUE(0); - Smallog.Debug("init...", ClassName); + this.icueMessage('LED: waiting for plugin.', true); + this.initCUE(0); + Smallog.debug('init...', ClassName); - // recreate if reinit - if (this.icueInterval) clearInterval(this.icueInterval); - if (this.helperCanvas) document.body.removeChild(this.helperCanvas); - // setup canvas - this.helperCanvas = document.createElement("canvas"); - this.helperCanvas.id = "helpCvs"; - this.helperCanvas.width = canvasX; - this.helperCanvas.height = canvasY; - this.helperCanvas.style.display = "none"; - this.helperContext = this.helperCanvas.getContext("2d"); - document.body.appendChild(this.helperCanvas); + // recreate if reinit + if (this.icueInterval) clearInterval(this.icueInterval); + if (this.helperCanvas) document.body.removeChild(this.helperCanvas); + // setup canvas + this.helperCanvas = document.createElement('canvas'); + this.helperCanvas.id = 'helpCvs'; + this.helperCanvas.width = canvasX; + this.helperCanvas.height = canvasY; + this.helperCanvas.style.display = 'none'; + this.helperContext = this.helperCanvas.getContext('2d'); + document.body.appendChild(this.helperCanvas); - // update devices about every 33ms/30fps. iCue doesnt really support higher values - this.icueInterval = setInterval(() => this.updateFrame(), 1000 / 30); - } - + // update devices about every 33ms/30fps. iCue doesnt really support higher values + this.icueInterval = window.setInterval(() => this.updateFrame(), 1000 / 30); + } - // show or hide preview - public UpdateSettings(): Promise { - var sett = this.settings; - // create preview? - if (!this.preview && sett.icue_area_preview) { - this.preview = document.createElement("div"); - this.preview.classList.add("cuePreview"); - document.body.appendChild(this.preview); - } - // update settings or destroy - if (this.preview) { - if (!sett.icue_area_preview) { - document.body.removeChild(this.preview); - this.preview = null; - } - else Object.assign(this.preview.style, this.getArea(true)); - } - return; - } - // will initialize ICUE api & usage - private initCUE(count) { - // wait for plugins - if (!this.isAvailable) { - if (count < 100) setTimeout(() => this.initCUE(++count), 300); - else this.icueMessage("LED: Plugin not found!"); - return; - } - // setup devices - this.icueDevices = []; + /** + * show or hide preview + * @return {Promise} finished + */ + public updateSettings(): Promise { + const sett = this.settings; + // create preview? + if (!this.preview && sett.icue_area_preview) { + this.preview = document.createElement('div'); + this.preview.classList.add('cuePreview'); + document.body.appendChild(this.preview); + } + // update settings or destroy + if (this.preview) { + if (!sett.icue_area_preview) { + document.body.removeChild(this.preview); + this.preview = null; + } else Object.assign(this.preview.style, this.getArea(true)); + } + return Promise.resolve(); + } - window['cue'].getDeviceCount((deviceCount) => { - this.icueMessage("LED: Found " + deviceCount + " devices."); - for (var xi = 0; xi < deviceCount; xi++) { - var xl = xi; - window['cue'].getDeviceInfo(xl, (info) => { - info.id = xl; - window['cue'].getLedPositionsByDeviceIndex(xl, function (leds) { - info.leds = leds; - this.icueDevices[xl] = info; - }); - }); - } - }); - } + /** + * will initialize ICUE api & usage + * @param {number} count Retries (will stop at 100) + */ + private initCUE(count) { + // wait for plugins + if (!this.isAvailable) { + if (count < 100) setTimeout(() => this.initCUE(++count), 300); + else this.icueMessage('LED: Plugin not found!'); + return; + } + // setup devices + this.icueDevices = []; - // do the thing... - private updateFrame() { - var sett = this.settings; - if (this.PAUSED || !this.isAvailable || sett.icue_mode == 0 || this.icueDevices.length < 1) return; - // projection mode - if (sett.icue_mode == 1) { - // get scaled down image data and encode it for icue - var encDat = this.getEncodedCanvasImageData(this.helperContext.getImageData(0, 0, canvasX, canvasY)); - // update all icueDevices with data - for (var xi = 0; xi < this.icueDevices.length; xi++) { - window['cue'].setLedColorsByImageData(xi, encDat, canvasX, canvasY); - } - } - // color mode - if (sett.icue_mode == 2) { - // get lol objects - var col = sett.icue_main_color.split(" ") as unknown[]; - var ledColor = { - r: col[0] as number * 255, - g: col[1] as number * 255, - b: col[2] as number * 255 - };; - // try audio multiplier processing - if (this.weas.hasAudio()) { - var aud = this.weas.lastAudio; - var mlt = 255 * aud.average / aud.range / aud.intensity * 10; - ledColor = { - r: Math.min(255, Math.max(0, col[0] as number * mlt)), - g: Math.min(255, Math.max(0, col[1] as number * mlt)), - b: Math.min(255, Math.max(0, col[2] as number * mlt)) - }; - } - // update all icueDevices with data - for (var xi = 0; xi < this.icueDevices.length; xi++) { - window['cue'].setAllLedsColorsAsync(xi, ledColor); - } - } - } + window['cue'].getDeviceCount((deviceCount) => { + this.icueMessage('LED: Found ' + deviceCount + ' devices.'); + for (let xi = 0; xi < deviceCount; xi++) { + const xl = xi; + window['cue'].getDeviceInfo(xl, (info) => { + info.id = xl; + window['cue'].getLedPositionsByDeviceIndex(xl, (leds) => { + info.leds = leds; + this.icueDevices[xl] = info; + }); + }); + } + }); + } - // prepare canvas - public updateCanvas(mainCanvas) { - var sett = this.settings; - if (!this.isAvailable || !mainCanvas || sett.icue_mode == 0 || this.icueDevices.length < 1) return; + /** + * do the thing... + */ + private updateFrame() { + const sett = this.settings; + if (this.PAUSED || !this.isAvailable || sett.icue_mode == 0 || this.icueDevices.length < 1) return; + // projection mode + if (sett.icue_mode == 1) { + // get scaled down image data and encode it for icue + const encDat = this.getEncodedCanvasImageData(this.helperContext.getImageData(0, 0, canvasX, canvasY)); + // update all icueDevices with data + for (let xi = 0; xi < this.icueDevices.length; xi++) { + window['cue'].setLedColorsByImageData(xi, encDat, canvasX, canvasY); + } + } + // color mode + if (sett.icue_mode == 2) { + // get lol objects + const col = sett.icue_main_color.split(' ') as unknown[]; + let ledColor = { + r: col[0] as number * 255, + g: col[1] as number * 255, + b: col[2] as number * 255, + }; ; + // try audio multiplier processing + if (this.weas.hasAudio()) { + const aud = this.weas.lastAudio; + const mlt = 255 * aud.average / aud.range / aud.intensity * 10; + ledColor = { + r: Math.min(255, Math.max(0, col[0] as number * mlt)), + g: Math.min(255, Math.max(0, col[1] as number * mlt)), + b: Math.min(255, Math.max(0, col[2] as number * mlt)), + }; + } + // update all icueDevices with data + for (let xi = 0; xi < this.icueDevices.length; xi++) { + window['cue'].setAllLedsColorsAsync(xi, ledColor); + } + } + } - if (sett.icue_mode == 1) { - // get helper vars - var area: any = this.getArea(false); - var hctx = this.helperContext; - // get real rgb values - var spl = sett.main_color.split(' ') as unknown[]; - for (var i = 0; i < spl.length; i++) spl[i] = (spl[i] as number * 255); - // overlay "decay" style - hctx.fillStyle = "rgba(" + spl.join(", ") + ", " + sett.icue_area_decay / 100 + ")"; - hctx.fillRect(0, 0, canvasX, canvasY); - // scale down and copy the image to the helper canvas - hctx.drawImage(mainCanvas, area.left, area.top, area.width, area.height, 0, 0, canvasX, canvasY); - // blur the helper projection canvas - if (sett.icue_area_blur > 0) this.gBlurCanvas(this.helperCanvas, hctx, sett.icue_area_blur); - } - } + /** + * copy main canvas portion to our helper + * @param {HTMLCanvasElementq} mainCanvas + */ + public updateCanvas(mainCanvas: HTMLCanvasElement) { + const sett = this.settings; + if (!this.isAvailable || !mainCanvas || sett.icue_mode == 0 || this.icueDevices.length < 1) return; + if (sett.icue_mode == 1) { + // get helper vars + const area: any = this.getArea(false); + const hctx = this.helperContext; + // get real rgb values + const spl = sett.main_color.split(' ') as unknown[]; + for (let i = 0; i < spl.length; i++) spl[i] = (spl[i] as number * 255); + // overlay "decay" style + hctx.fillStyle = 'rgba(' + spl.join(', ') + ', ' + sett.icue_area_decay / 100 + ')'; + hctx.fillRect(0, 0, canvasX, canvasY); + // scale down and copy the image to the helper canvas + hctx.drawImage(mainCanvas, area.left, area.top, area.width, area.height, 0, 0, canvasX, canvasY); + // blur the helper projection canvas + if (sett.icue_area_blur > 0) this.gBlurCanvas(this.helperCanvas, hctx, sett.icue_area_blur); + } + } } diff --git a/src/WEWA.ts b/src/WEWA.ts new file mode 100644 index 0000000..0b6bc0b --- /dev/null +++ b/src/WEWA.ts @@ -0,0 +1,1104 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +import {waitReady} from './Util'; +import {Smallog} from './Smallog'; +import {OfflineHelper} from './offline/OfflineHelper'; +import {CC} from 'cookieconsent'; +import {myFetch} from './wasc-worker/WascRT'; + +const LogHead = '[WEWWA] '; +const DefLang = 'de-de'; + +/** +* WEWWA +*
    +* Wallpaper Engine Web Wallpaper Adapter +*
    +* This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine +* Web-Wallpaper project - so you can test, run & configure it from a normal web browser. +*
    +* REQUIREMENTS: +*
    +* - HTML5 Browser +*
    +* - the "project.json" needs to be in the root folder like "index.html" +*
    +* - this file needs to be included/built in your "index.html" +*
    +*
    +* FEATURES: +*
    +* - automatically detecting if the web wallpaper is opened by wallpaper engine or browser +*
    +* - if opened by wallpaper engine, nothing will happen +*
    +* - if opened by a browser: +*
    +* - use a ServiceWorker to make page always available offline +*
    +* - automatically load the "project.json" +*
    +* - parse the settings, languages & conditions +*
    +* - add respective html elements for each setting type & condition +*
    +* - put these elements into an option menu which can be hidden +*
    +* - check localStorage for already saved/customized values +*
    +* - apply all settings once +*
    +* - react to changes made in the ui and update them in the wallpaper +*
    +* - save changes made in the ui to localStorage +*
    +* - Annoying Cookie Popup (Thanks DSGVO) +* +* +* @todo +* - inject "audio processing" setting +* +* lighthouse: +* - image explicit width/height +* - cf longer cache policy (2d?) +* - iniitialize, else => do nothing + * @param {Function} finished Callback + */ + constructor(finished) { + if (window['wallpaperRegisterAudioListener']) { + Smallog.info('detected wallpaper engine => Standby.', LogHead); + finished(); + return; + } + + Smallog.info('wallpaper engine not detected => Init!', LogHead); + + // define audio listener first, so we dont miss when it gets registered. + window['wallpaperRegisterAudioListener'] = (callback) => { + // set callback to be called later with analysed audio data + this.audioCallback = callback; + Smallog.info('Registered wallpaper AudioListener.', LogHead); + }; + + // intialize when ready + waitReady().then(() => { + if (CC) {/* This tells the compiler to include CookieConsent at this point. */} + + // Thanks DSGVO... + window['cookieconsent'].initialise({ + palette: { + popup: {background: '#000'}, + button: {background: '#f1d600'}, + }, + position: 'bottom-left', + theme: 'edgeless', + }); + + // make the website available offline using service worker + OfflineHelper.register(document.title.replace(' ', '')).then(() => { + // continue initializing + finished(); + this.init(); + + // pause and resume on focus events + window.onblur = () => this.setPaused(true); + window.onfocus = () => this.setPaused(false); + }); + }); + } + + /** + * Initialize the Web Adapter + */ + private init() { + myFetch('project.json', 'json').then((proj) => { + if (proj.type != 'web') { + Smallog.Error('Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...', LogHead); + return; + } + this.project = proj; + this.loadStorage(); + this.addStyle(); + this.addMenu(localStorage.getItem('wewwaLang')); + this.evaluateSettings(); + this.applyProp(proj.general.properties); + }); + } + + /** + * Load last settings from localStorage + */ + private loadStorage() { + const props = this.project.general.properties; + const last = localStorage.getItem('wewwaLastProps'); + if (last != null) { + const merged = Object.assign(props, JSON.parse(last)); + merged.audioprocessing = { + value: this.project.general.supportsaudioprocessing, + type: 'hidden', + }; + this.project.general.properties = merged; + Smallog.debug('Loaded & merged settings.', LogHead); + } + } + + /** + * CSS Insertion + */ + private addStyle() { + const st = document.createElement('style'); + // precalculation + const minWidthPx = 420; + const percentageWidth = 20; + const pwShort = `${percentageWidth}vw`; + st.innerHTML = ` + #wewwaMenu, #wewwaIcon { + transform: none; + transition: transform 500ms ease; + position:absolute; + top:0px; + padding:15px; + z-index:9999; + } + #wewwaMenu { + top:10px; + border: solid 2px #444; + width:${pwShort}; + left:100vw; + color:white; + background-color: rgba(0.6,0.6,0.6,0.8); + overflow-x:hidden; + overflow-y:scroll; + max-height:92.5%; + min-width: ${minWidthPx}px; + max-width: 100vw; + font-family: Helvetica, Verdana, Arial; + font-size: larger; + } + #wewwaMenu hr { + margin: 20px 0px; + } + #wewwaMenu a { + color: white; + border: 2px solid #4CAF50; + padding: 5px 10px; + margin: 5px; + text-decoration: none; + display: block; + } + #wewwaMenu a:hover { + background: #4CAF50; + } + #wewwaMenu .red { + border-color: #FF7F50; + } + #wewwaMenu .red:hover { + background-color: #FF7F50; + } + #wewwaMenu .audio { + border-color: #00a1ff; + } + #wewwaMenu .audio:hover { + background-color: #00a1ff; + } + #wewwaMenu audio, #wewwaMenu select { + width: 100%; + } + #wewwaMenu table { + width:100%; + table-layout: fixed; + } + #wewwaMenu tr.hide { + display: none; + } + #wewwaMenu td { + width: 50%; + padding: 5px; + } + #wewwaMenu .left { + text-align: left; + } + #wewwaMenu .right { + text-align: right; + } + #wewwaMenu img { + width: ${percentageWidth / 2}vw; + min-width: ${Math.floor(minWidthPx / 2)}px; + max-width: 90%; + heigth: auto; + } + #wewwaMenu .droparea { + border: 2px dashed #bbb; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 20px; + text-align: center; + font: 18pt; + color: #bbb; + } + /* Icon */ + #wewwaIcon { + right:0px; + cursor:pointer; + } + #wewwaIcon div { + width:35px; + height:5px; + background-color:#888888; + margin:6px 0; + } + + #wewwaMenu.open, #wewwaIcon.open { + transform: translateX(min(-${percentageWidth * 1.1}vw, -${Math.floor(minWidthPx * 1.1)}px)); + transition: transform 500ms ease; + } + + /* Smartphone format */ + @media all and (max-width: 1000px) { + #wewwaMenu { + width:90vw; + } + #wewwaMenu.open { + transform: translateX(-95vw); + transition: transform 500ms ease; + } + #wewwaIcon.open { + transform: translateX(calc(-100vw + 60px)); + transition: transform 500ms ease; + } + } + `; + document.head.append(st); + } + + /** + * HTML Creation + * @param {string} lang WE language + */ + private addMenu(lang) { + const self = this; + if (this.htmlMenu) { + document.body.removeChild(this.htmlMenu); + document.body.removeChild(this.htmlIcon); + this.htmlMenu = null; + } + + // quick wrapper, we need this a lot + const ce = (e) => document.createElement(e); + + // local vars faster + const proj = this.project; + const props = proj.general.properties; + + // create root menu + this.htmlMenu = ce('div'); + this.htmlMenu.id = 'wewwaMenu'; + + // create preview img wrap + this.addMenuHeader(ce, proj); + // create table with settings + this.addMenuSettings(ce, proj, self, lang, props); + // Add Footer + this.addMenuFooter(ce); + // finally add the menu to the DOM + document.body.append(this.htmlMenu); + + // last create the icon for opening & closing the menu + this.addMenuIcon(ce); + } + + /** + * Adds the Menu Icon + * @param {Function} ce CreateElement + * @param {Element} menu + */ + private addMenuIcon(ce: (e: any) => any, menu = this.htmlMenu) { + const icon = this.htmlIcon = ce('div'); + icon.id = 'wewwaIcon'; + icon.addEventListener('click', () => { + if (this.htmlMenu.classList.contains('open')) { + this.htmlMenu.classList.remove('open'); + } else { + this.htmlMenu.classList.add('open'); + } + if (icon.classList.contains('open')) { + icon.classList.remove('open'); + } else { + icon.classList.add('open'); + } + }); + const bar1 = ce('div'); + const bar2 = ce('div'); + const bar3 = ce('div'); + icon.append(bar1, bar2, bar3); + document.body.append(icon); + } + + /** + * Adds the actual Wallpaper Props as HTML + * @param {Function} ce Create Element wrapper + * @param {Object} proj project + * @param {object} self this + * @param {string} lang + * @param {object} props + * @param {Element} menu + */ + private addMenuSettings(ce: (e: any) => any, proj: any, self: this, lang: string, props: any, menu = this.htmlMenu) { + const tbl = ce('table'); + tbl.innerHTML = ' '; + const tblBody = ce('tbody'); + tbl.append(tblBody); + + // if app supports audio, add input menu & handlers + if (proj.general.supportsaudioprocessing) { + this.addMenuAudio(ce, tblBody); + } + + // create actual settings wrapper + const settings = ce('tr'); + settings.innerHTML = '

    Settings


    '; + tblBody.append(settings); + + // pause checkbox + const pauseRow = ce('tr'); + const pauseOne = ce('td'); + pauseOne.innerHTML = '

    Pause on Unfocus

    '; + const pauseTwo = ce('td'); + pauseTwo.setAttribute('colspan', '2'); + const pauseBox = ce('input'); + pauseBox.setAttribute('type', 'checkbox'); + pauseBox.setAttribute('checked', this.pauseOnUnfocus); + pauseBox.addEventListener('change', function(e) { + // eslint-disable-next-line no-invalid-this + self.pauseOnUnfocus = this.checked; + // unpause if paused + if (!self.pauseOnUnfocus && self.isPaused) { + self.setPaused(false); + } + }); + pauseTwo.append(pauseBox); + pauseRow.append(pauseOne, pauseTwo); + tblBody.append(pauseRow); + + // language select? + const local = proj.general.localization; + if (local) { + // set default language + if (!lang) { + lang = DefLang; + } + // add default strings + this.mergeLocals(local); + // add language menu row + const row = this.makeMenuLocalization(ce, lang, local, props); + tblBody.append(row); + } + + // split content from actual settings + const splitr = ce('tr'); + splitr.innerHTML = '
    '; + tblBody.append(splitr); + + // sort settings by order + const sortable = []; + for (const p in props) { + if (p) sortable.push([p, props[p]]); + } + sortable.sort((a, b) => a[1].order - b[1].order); + // add setting html elements + for (const s of sortable) { + const itm = this.createItem(s[0], s[1]); + if (itm) tblBody.append(itm); + } + + // pre-footer for resetting saved settings + // finish up menu + menu.append(tbl); + } + + /** + * Add missing default localization strings + * @param {Object} local + */ + private mergeLocals(local: any) { + const locDefs = { + 'ui_browse_properties_scheme_color': 'Scheme color', + }; + for (const loc in local) { + if (!local[loc]) continue; + for (const def in locDefs) { + if (!local[loc][def]) { + local[loc][def] = locDefs[def]; + } + } + } + } + + /** + * Adds the Footer Link to the Menu + * @param {Function} ce create element + * @param {Element} menu + */ + private addMenuFooter(ce: (e: any) => any, menu = this.htmlMenu) { + const preFoot = ce('div'); + preFoot.innerHTML = '
    '; + + const reset = ce('a'); + reset.classList.add('red'); + reset.innerHTML = 'Reset ↩️'; + reset.addEventListener('click', (e) => { + if (!window.confirm('This action will clear ALL local data!\r\n\r\nAre you sure?')) { + return; + } + OfflineHelper.reset().then(() => { + localStorage.clear(); + location = location; + }); + }); + preFoot.append(reset); + + // footer with ident + const footer = ce('div'); + footer.innerHTML = ` +
    +

    + [W]allpaper
    + [E]ngine
    + [W]eb
    + [A]dapter +

    + by hexxone + `; + + menu.append(preFoot, footer); + } + + // eslint-disable-next-line valid-jsdoc + /** + * Add Language Menu + */ + private makeMenuLocalization(ce: (e: any) => any, lang, local, props) { + const self = this; + // add html struct + const row = ce('tr'); + const td1 = ce('td'); + td1.innerHTML = '

    🌍

    '; + const td2 = ce('td'); + const lan = ce('select'); + // process all + for (const loc in local) { + if (!loc) continue; + // build select option for this + const lcs = ce('option'); + lcs.value = loc; + lcs.innerHTML = loc.toUpperCase(); + lan.append(lcs); + // check for correct language code + if (loc != lang) continue; + else lcs.setAttribute('selected', 'true'); + // set properties translated text + for (const p in props) { + if (!p) continue; + const itm = props[p]; + const pTxt = itm.text; + const rTxt = local[loc][pTxt]; + if (rTxt) itm.realText = rTxt; + // process combo box values + if (itm.type == 'combo') { + for (const o of itm.options) { + const lTxt = local[loc][o.label]; + if (lTxt) o.realLabel = lTxt; + } + } + } + } + // if changed, do it all over again. + lan.addEventListener('change', function(e) { + // eslint-disable-next-line no-invalid-this + localStorage.setItem('wewwaLang', this.value); + // eslint-disable-next-line no-invalid-this + self.addMenu(this.value); + self.evaluateSettings(); + (self.htmlIcon as any).click(); + }); + td2.setAttribute('colspan', '2'); + td2.append(lan); + row.append(td1, td2); + return row; + } + + // eslint-disable-next-line valid-jsdoc + /** + * Add Audio Menu + */ + private addMenuAudio(ce: (e: any) => any, tblBody: any) { + // audio input methods + const row = ce('tr'); + + const td1 = ce('td'); + td1.innerHTML = '

    Audio Input


    '; + td1.setAttribute('colspan', '3'); + + // Microphone input + const aBtn1 = ce('a'); + aBtn1.classList.add('audio'); + aBtn1.innerHTML = 'Microphone'; + aBtn1.addEventListener('click', (e) => { + this.requestMicrophone(); + }); + + // File Url input + const aBtn2 = ce('a'); + aBtn2.classList.add('audio'); + aBtn2.innerHTML = 'Select URL'; + aBtn2.addEventListener('click', (e) => { + const uri = prompt('Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!', 'https://example.com/test.mp3'); + this.initiateAudio(uri); + }); + + // System file input + const aBtn3 = ce('input'); + aBtn3.id = 'wewwaAudioInput'; + aBtn3.innerHTML = 'Select File'; + aBtn3.setAttribute('type', 'file'); + aBtn3.addEventListener('change', (e) => { + const file = (e.target as any).files[0]; + if (!file) { + return; + } + this.initiateAudio(file); + }); + + td1.append(aBtn1, aBtn2, aBtn3); + row.append(td1); + + + // file drag & drop area + const dropRow = ce('tr'); + const dropCol1 = ce('td'); + const dropCol2 = ce('td'); + dropCol1.setAttribute('colspan', '3'); + + const dropArea = ce('div'); + dropArea.innerHTML = 'Drag & Drop'; + dropArea.classList.add(...['droparea', 'audio']); + dropArea.addEventListener('dragover', (evt) => { + evt.stopPropagation(); + evt.preventDefault(); + evt.dataTransfer.dropEffect = 'copy'; + }, false); + dropArea.addEventListener('drop', (e) => { + e.stopPropagation(); + e.preventDefault(); + const droppedFiles = e.dataTransfer.files; + this.initiateAudio(droppedFiles[0]); + }, false); + dropCol1.append(dropArea); + dropRow.append(dropCol1, dropCol2); + + + // Play & Stop Btn + const hrrow = ce('tr'); + const hrtd1 = ce('td'); + hrtd1.id = 'audioMarker'; + hrtd1.setAttribute('colspan', '3'); + const stopBtn = ce('a'); + stopBtn.classList.add('red'); + stopBtn.innerHTML = 'Stop All Audio'; + stopBtn.addEventListener('click', (e) => { + this.stopAudioInterval(); + }); + hrtd1.append(stopBtn); + const hrtd2 = ce('td'); + hrrow.append(hrtd1, hrtd2); + + // finally add rows to table + tblBody.append(row, dropRow, hrrow); + } + + // eslint-disable-next-line valid-jsdoc + /** + * Add preview Image, Title and Link + */ + private addMenuHeader(ce: (e: any) => any, proj: any, menu = this.htmlMenu) { + const preview = ce('img'); + preview.setAttribute('src', proj.preview); + // create menu app title + const header = ce('div'); + header.innerHTML = '

    ' + proj.title + '

    '; + // create workshop link + const link = ce('a'); + link.setAttribute('rel', 'noreferrer'); + link.setAttribute('href', 'https://steamcommunity.com/sharedfiles/filedetails/?id=' + proj.workshopid); + link.setAttribute('target', '_blank'); + link.innerHTML = '

    Open Workshop Page

    '; + menu.append(preview, header, link); + } + + // eslint-disable-next-line valid-jsdoc + /** + * Create an HTML Menu Item from project json property + */ + private createItem(prop, itm) { + if (!itm.type || itm.type == 'hidden') return null; + const self = this; + const ce = (e) => document.createElement(e); + // table structure + const row = ce('tr'); + row.setAttribute('id', 'wewwa_' + prop); + // Text + const column1 = ce('td'); + column1.classList.add('left'); + // Input + const column2 = ce('td'); + column2.classList.add('right'); + // optional NumericUpDown Column + let column3 = null; + // div or label text element + let txt = null; + // main input element + let inpt = null; + + // Process actual prop type + switch (itm.type) { + // only text across 3 columns + case 'text': + txt = ce('div'); + txt.innerHTML = itm.realText ? itm.realText : itm.text; + column1.setAttribute('colspan', 3); + break; + + // combo select-box across 2 columns + case 'combo': + inpt = ce('select'); + // set options + for (const o of itm.options) { + const opt = ce('option'); + opt.setAttribute('value', o.value); + opt.innerText = o.realLabel ? o.realLabel : o.label; + if (itm.value == o.value) opt.setAttribute('selected', true); + inpt.appendChild(opt); + } + break; + + // system color picker across 2 columns + case 'color': + inpt = ce('input'); + inpt.setAttribute('type', 'color'); + break; + + // Checkbox across 2 columns + case 'bool': + inpt = ce('input'); + inpt.setAttribute('type', 'checkbox'); + inpt.setAttribute('readonly', true); + break; + + // Slider input across 1 column; + 1 column Up/Down + case 'slider': + const canEdit = itm.editable; + // create numeric-up-down + const sliderVal = ce(canEdit ? 'input' : 'output'); + sliderVal.name = 'wewwa_out_' + prop; + sliderVal.setAttribute('id', sliderVal.name); + sliderVal.setAttribute('type', 'number'); + sliderVal.style.width = '75%'; + if (canEdit) { + sliderVal.setAttribute('value', itm.value); + sliderVal.addEventListener('change', function(e) { + // eslint-disable-next-line no-invalid-this + self.setProperty(prop, this); + }); + } else { + sliderVal.innerHTML = itm.value; + } + // create td3 + column3 = ce('td'); + column3.append(sliderVal); + // create actual slider & values + inpt = ce('input'); + inpt.setAttribute('type', 'range'); + inpt.max = itm.max; + inpt.min = itm.min; + inpt.step = 0.1; + break; + + // Text input across 2 columns + case 'textinput': + inpt = ce('input'); + inpt.setAttribute('type', 'text'); + break; + + // File input across 2 columns + case 'file': + inpt = ce('input'); + inpt.setAttribute('type', 'file'); + break; + + default: + Smallog.Error('unkown setting type: ' + itm.type, LogHead); + break; + } + + const eid = 'wewwa_prop_' + prop; + + // make input label if not text + if (!txt) { + txt = ce('label'); + txt.setAttribute('for', eid); + txt.innerHTML = itm.realText ? itm.realText : itm.text; + } + column1.append(txt); + + // listen for changes if input type (no text) + if (inpt) { + inpt.style.width = '100%'; + inpt.setAttribute('id', eid); + inpt.addEventListener('change', function(e) { + // eslint-disable-next-line no-invalid-this + self.setProperty(prop, this); + }); + column2.prepend(inpt); + } + + // append td3 or stretch td2? + row.append(column1, column2); + if (column3) row.append(column3); + else column2.setAttribute('colspan', 2); + + return row; + } + + + // ------------------------------------- + // Settings Helper + // ------------------------------------- + + // eslint-disable-next-line valid-jsdoc + /** + * Callback for UI-Settings changes + * Will apply them to the storage and running wallaper. + */ + public setProperty(prop, elm) { + // get the type and apply the value + const props = this.project.general.properties; + + // check for legit setting... + if (!props[prop]) { + Smallog.Error('SetProperty name not found: ' + prop, LogHead); + return; + } + + // enabled delayed call of settings update + const applyCall = (val) => { + // save the updated value to storage + props[prop].value = val; + // update + this.evaluateSettings(); + const obj = {}; + obj[prop] = props[prop]; + this.applyProp(obj); + }; + + // process value based on DOM element type + switch (props[prop].type) { + case 'bool': + applyCall(elm.checked == true); + break; + case 'color': + applyCall(this.hexToRgb(elm.value)); + break; + case 'file': + this.loadXHRSaveLocal(elm.value, (res) => applyCall(res)); + break; + case 'slider': + if (elm.name.includes('_out_')) { + const inpt: any = document.querySelector('#wewwa_' + prop); + if (inpt) inpt.value = elm.value; + else Smallog.Error('Slider not found: ' + prop, LogHead); + } else { + const slide: any = document.querySelector('#wewwa_out_' + prop); + if (slide) slide.value = elm.value; + else Smallog.Error('Numericupdown not found: ' + prop, LogHead); + } + case 'combo': + case 'textinput': + applyCall(elm.value); + break; + } + } + + // eslint-disable-next-line valid-jsdoc + /** + * will load the given file and return it as dataURL. + * this way we can easily store whole files in the configuration & localStorage. + * its not safe that this works with something else than image files. + */ + private loadXHRSaveLocal(url, resCall) { + myFetch(url, 'blob').then((resp) => { + // Read out file contents as a Data URL + const fReader = new FileReader(); + // onload needed since Google Chrome doesn't support addEventListener for FileReader + fReader.onload = (evt) => resCall(evt.target.result); + // Load blob as Data URL + fReader.readAsDataURL(resp); + }); + } + + /** + * Show or hide menu items based on eval condition + */ + public evaluateSettings() { + const wewwaProps = this.project.general.properties; + localStorage.setItem('wewwaLastProps', JSON.stringify(wewwaProps)); + for (const p in wewwaProps) { + if (!p) continue; + const prop = wewwaProps[p]; + + // some ev(a|i)l magic + let visible = true; + if (prop.condition != null) { + // copy our condition string to modify + let cprop = String(prop.condition).split(' ').join(''); + // remove whitespaces and split to partials by logic operators + const partials = cprop.split(/&&|\|\|/); + // loop all partial values of the check + for (const part of partials) { + let prefix = 'wewwaProps.'; + const onlyVal = part.match(/[!a-zA-Z0-9_\.]*/)[0]; + if (!onlyVal.startsWith(prefix) && !onlyVal.startsWith('!' + prefix)) { + // fix for inverted values + let replW = onlyVal; + if (replW.startsWith('!')) { + replW = replW.substr(1); + prefix = '!' + prefix; + } + // Smallog.Debug("replace: " + onlyVal + " >> " + prefix + replW); + cprop = cprop.replace(onlyVal, prefix + replW); + } + } + try { + visible = eval(cprop) == true; + } catch (e) { + Smallog.Error('Error: (' + cprop + ') for: ' + p + ' => ' + e, LogHead); + } + } + + // get input dom element + const htElm = document.getElementById('wewwa_' + p); + if (!htElm || htElm.childNodes.length < 2) continue; + + if (visible) htElm.classList.remove('hide'); + else htElm.classList.add('hide'); + + // set its value + const elm: any = htElm.childNodes[1].childNodes[0]; + switch (prop.type) { + case 'color': + elm.value = this.rgbToHex(prop.value); + break; + case 'bool': + elm.checked = prop.value == true; + break; + case 'slider': + case 'combo': + case 'textinput': + elm.value = prop.value; + break; + } + } + } + + + // ------------------------------------- + // Wallpaper Interface + // ------------------------------------- + + // eslint-disable-next-line valid-jsdoc + /** + * Send one or more properties to the Wallpaper + */ + public applyProp(prop) { + const wpl = window['wallpaperPropertyListener']; + if (wpl && wpl.applyUserProperties) { + wpl.applyUserProperties(prop); + } + } + + // eslint-disable-next-line valid-jsdoc + /** + * Send paused-status to the Wallpaper + */ + public setPaused(val: boolean) { + const wpl = window['wallpaperPropertyListener']; + if (this.isPaused == val) return; + if (val && !this.pauseOnUnfocus) return; + if (wpl && wpl.setPaused) { + wpl.setPaused(val); + this.isPaused = val; + } + } + + + // ------------------------------------- + // UI Color Input conversion + // ------------------------------------- + + // eslint-disable-next-line require-jsdoc + private rgbToHex(rgb) { + // eslint-disable-next-line require-jsdoc + function cth(c) { + const h = Math.floor(c * 255).toString(16); + return h.length == 1 ? '0' + h : h; + } + const spl = rgb.split(' '); + return '#' + cth(spl[0]) + cth(spl[1]) + cth(spl[2]); + } + + // eslint-disable-next-line require-jsdoc + private hexToRgb(hex) { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255].join(' ') : null; + } + + // ------------------------------------- + // AUDIO PROCESSING + // ------------------------------------- + + /** + * Request microphone from browser + */ + private requestMicrophone() { + navigator.mediaDevices.getUserMedia({ + audio: true, + }).then((stream) => { + this.stopAudioInterval(); + // hack for firefox to keep stream running + window['persistAudioStream'] = stream; + this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); + this.source = this.ctx.createMediaStreamSource(stream); + this.analyser = this.ctx.createAnalyser(); + this.analyser.smoothingTimeConstant = 0.35; + this.analyser.fftSize = 256; + + this.source.connect(this.analyser); + this.startAudioInterval(); + }).catch((err) => { + Smallog.Error(err, LogHead); + if (location.protocol != 'https:') { + const r = confirm('Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS for this feature to work! Press \'ok\'/\'yes\' to get redirected to HTTPS and try again.'); + if (r) window.location.href = window.location.href.replace('http', 'https'); + } + }); + } + + // eslint-disable-next-line valid-jsdoc + /** + * html5 audio analyser gives us mono data from 0(bass) to 128(treble) + * however, wallpaper engine expects stereo data in following format: + * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) + * so we do some array transformation... and divide by 255 (8bit-uint becomes float) + */ + private convertAudio(data) { + const stereo = []; + let sIdx = 0; + for (let i = 0; i < 64; i++) { + stereo[i] = data[sIdx++] / 255; + stereo[127 - i] = data[sIdx++] / 255; + } + return stereo; + } + + // eslint-disable-next-line valid-jsdoc + /** + * Start the audio processing & analyzer + */ + private initiateAudio(data) { + // clear up + this.stopAudioInterval(); + // create player + this.audio = document.createElement('audio'); + this.audio.src = data.name ? URL.createObjectURL(data) : data; + this.audio.autoplay = true; + this.audio.setAttribute('controls', 'true'); + this.audio.play(); + + // insert before marker + const markr = document.getElementById('audioMarker'); + markr.parentElement.insertBefore(this.audio, markr); + + this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); + this.source = this.ctx.createMediaElementSource(this.audio); + this.analyser = this.ctx.createAnalyser(); + this.analyser.smoothingTimeConstant = 0.35; + this.analyser.fftSize = 256; + + this.source.connect(this.ctx.destination); + this.source.connect(this.analyser); + this.startAudioInterval(); + } + + /** + * Start the processing loop + */ + private startAudioInterval() { + const data = new Uint8Array(128); + // 33ms ~~ 30fps + this.audioInterval = window.setInterval(() => { + if (this.audioCallback == null) { + this.stopAudioInterval(); + Smallog.Error('no AudioCallback!', LogHead); + return; + } + this.analyser.getByteFrequencyData(data); + const stereo = this.convertAudio(data); + this.audioCallback(stereo); + }, 33); + // tell Wallpaper we are sending audio + this.applyProp({audioprocessing: {value: true}}); + } + + /** + * Stop the processing loop + */ + public stopAudioInterval() { + window['persistAudioStream'] = null; + document.getElementById('wewwaAudioInput').setAttribute('value', ''); + if (this.audio) { + this.audio.remove(); + } + if (this.audioInterval) { + clearInterval(this.audioInterval); + this.audioInterval = null; + } + } +} diff --git a/src/WEWWA.ts b/src/WEWWA.ts deleted file mode 100644 index c781dba..0000000 --- a/src/WEWWA.ts +++ /dev/null @@ -1,834 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @see - * REQUIREMENTS: - * - JQUERY >= 3.3.1 - * - HTML5 Browser - * - the "project.json" needs to be in the root folder like "index.html" - * - this file needs to be included/loaded in your "index.html" - * - * @description - * WEWWA - * Wallpaper Engine Web Wallpaper Adapter - * - * This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine - * Web-Wallpaper project - so you can test, run & configure it from a normal web browser. - * - * FEATURES: - * - automatically detecting if the web wallpaper is opened by wallpaper engine or browser - * - if opened by wallpaper engine, nothing will happen - * - if opened by a browser: - * - use a ServiceWorker to make page always available offline - * - automatically load the "project.json" - * - parse the settings, languages & conditions - * - add respective html elements for each setting type & condition - * - put these elements into an option menu which can be hidden - * - check localStorage for already saved/customized values - * - apply all settings once - * - react to changes made in the ui and update them in the wallpaper - * - save changes made in the ui to localStorage - * - Annoying Cookie Popup (Thanks DSGVO) - * - * @todo - * - inject "audio processing" setting - * - * lighthouse: - * - image explicit width/height - * - cf longer cache policy (2d?) - * - Standby.", LogHead); - finished(); - return; - } - - Smallog.Info("wallpaper engine not detected => Init!", LogHead); - - // define audio listener first, so we dont miss when it gets registered. - window['wallpaperRegisterAudioListener'] = (callback) => { - // set callback to be called later with analysed audio data - this.audioCallback = callback; - } - - // intialize when ready - Ready().then(() => { - if (CC) { /* This tells the compiler to include CookieConsent at this point. */ } - window['cookieconsent'].initialise({ - palette: { - popup: { background: "#000" }, - button: { background: "#f1d600" } - }, - position: "bottom-left", - theme: "edgeless" - }); - - // make the website available offline using service worker - OfflineHelper.Register().then(() => { - // continue initializing - finished(); - this.Init(); - - // pause and resume on focus events - window.onblur = () => this.SetPaused(true); - window.onfocus = () => this.SetPaused(false); - }); - }); - } - - private Init() { - myFetch("project.json", "json").then(proj => { - - if (proj.type != "web") { - Smallog.Error("Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...", LogHead); - return; - } - this.project = proj; - this.LoadStorage(); - this.AddStyle(); - this.AddMenu(localStorage.getItem("wewwaLang")); - this.EvaluateSettings(); - this.ApplyProp(proj.general.properties); - }); - } - - private LoadStorage() { - var props = this.project.general.properties; - var last = localStorage.getItem("wewwaLastProps"); - if (last != null) { - var merged = Object.assign(props, JSON.parse(last)); - merged.audioprocessing = { - value: this.project.general.supportsaudioprocessing, - type: "hidden" - }; - this.project.general.properties = merged; - Smallog.Debug("Loaded & merged settings.", LogHead) - } - } - - private AddStyle() { - var st = document.createElement("style"); - st.innerHTML = ` - #wewwaMenu, #wewwaIcon { - transform: none; - transition: transform 500ms ease; - position:absolute; - top:0px; - padding:10px; - margin:10px; - z-index:9999; - } - #wewwaMenu { - border: solid 2px #444; - width:400px; - right:-440px; - color:white; - background-color:#333333; - overflow-x:hidden; - overflow-y:scroll; - max-height:95%; - max-width: 90%; - font-family: Helvetica, Verdana, Arial; - font-size: larger; - } - #wewwaMenu.open { - transform: translateX(-440px); - transition: transform 500ms ease; - } - @media all and (max-width: 520px) { - #wewwaMenu.open { - max-height:85%; - transform: translateX(-440px) translateY(55px); - transition: transform 500ms ease; - } - } - #wewwaMenu a { - color: white; - border: 2px solid #4CAF50; - padding: 5px 20px; - text-decoration: none; - display: inline-block; - } - #wewwaMenu a:hover { - background: #4CAF50; - } - #wewwaMenu .red { - border-color: #FF7F50; - } - #wewwaMenu .red:hover { - background-color: #FF7F50; - } - #wewwaMenu .orange { - border-color: #FFA500; - } - #wewwaMenu .orange:hover { - background-color: #FFA500; - } - #wewwaMenu audio { - width: 100%; - } - #wewwaMenu table { - width:100%; - table-layout: fixed; - } - #wewwaMenu tr { - visibility: collapse; - } - #wewwaMenu tr.show { - visibility: visible; - } - #wewwaMenu td { - width: 50%; - padding: 5px; - } - #wewwaMenu img { - width: 200px; - max-width: 90%; - heigth: auto; - } - #wewwaMenu input[type='checkbox'][readonly]{ - pointer-events: none; - } - #wewwaMenu .droparea { - border: 2px dashed #bbb; - -webkit-border-radius: 5px; - border-radius: 5px; - padding: 20px; - text-align: center; - font: 18pt; - color: #bbb; - } - #wewwaIcon { - right:0px; - cursor:pointer; - } - #wewwaIcon div { - width:35px; - height:5px; - background-color:#888888; - margin:6px 0; - } - @media all and (min-width: 520px) { - #wewwaIcon.open { - transform: translateX(-440px); - transition: transform 500ms ease; - } - } - `; - document.head.append(st); - } - - private AddMenu(lang) { - - if (this.htmlMenu) { - document.body.removeChild(this.htmlMenu); - document.body.removeChild(this.htmlIcon); - this.htmlMenu = null; - } - // quick wrapper, we need this a lot - const ce = (e) => document.createElement(e); - - // local vars faster - var proj = this.project; - var props = proj.general.properties; - - // create root menu - var menu = ce("div"); - menu.id = "wewwaMenu"; - // create preview img wrap - var preview = ce("img"); - preview.setAttribute("src", proj.preview); - // create menu app title - var header = ce("div"); - header.innerHTML = "

    " + proj.title + "

    "; - // create workshop link - var link = ce("a"); - link.setAttribute("rel", "noreferrer"); - link.setAttribute("href", "https://steamcommunity.com/sharedfiles/filedetails/?id=" + proj.workshopid); - link.setAttribute("target", "_blank"); - link.innerHTML = "

    Open Workshop Page

    "; - - // table is better for formatting - var tmain = ce("table") - tmain.innerHTML = " "; - var table = ce("tbody"); - tmain.append(table); - - // if app supports audio, add input menu & handlers - if (proj.general.supportsaudioprocessing) { - - // audio input methods - var row = ce("tr"); - row.classList.add("show"); - - var td1 = ce("td"); - td1.innerHTML = "

    Audio Input


    "; - td1.setAttribute("colspan", "3"); - - var aBtn1 = ce("a"); - aBtn1.classList.add("orange"); - aBtn1.innerHTML = "Microphone"; - aBtn1.addEventListener("click", e => { - this.requestMicrophone(); - }); - - var aBtn2 = ce("a"); - aBtn2.classList.add("orange"); - aBtn2.innerHTML = "Select URL"; - aBtn2.addEventListener("click", e => { - var uri = prompt("Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!", "https://example.com/test.mp3"); - this.initiateAudio(uri); - }); - - var aBtn3 = ce("input"); - aBtn3.id = "wewwaAudioInput"; - aBtn3.innerHTML = "Select File"; - aBtn3.setAttribute("type", "file"); - aBtn3.addEventListener("change", e => { - var file = (e.target as any).files[0]; - if (!file) return; - this.initiateAudio(file); - }); - - td1.append(aBtn1, aBtn2, aBtn3); - row.append(td1); - - // file drag & drop area - var dropRow = ce("tr"); - dropRow.classList.add("show"); - - var dropt1 = ce("td"); - var dropt2 = ce("td"); - dropt1.setAttribute("colspan", "3"); - var dropArea = ce("div"); - dropArea.innerHTML = "Drag & Drop" - dropArea.classList.add(...["droparea", "red"]); - dropArea.addEventListener('dragover', (evt) => { - evt.stopPropagation(); - evt.preventDefault(); - evt.dataTransfer.dropEffect = 'copy'; - }, false); - dropArea.addEventListener("drop", e => { - e.stopPropagation(); - e.preventDefault(); - const droppedFiles = e.dataTransfer.files; - this.initiateAudio(droppedFiles[0]); - }, false); - dropt1.append(dropArea); - dropRow.append(dropt1, dropt2); - - // Player & Stop Btn - var hrrow = ce("tr"); - hrrow.classList.add("show"); - - var hrtd1 = ce("td"); - var hrtd2 = ce("td"); - var hrstop = ce("a"); - hrstop.classList.add("red"); - hrstop.innerHTML = "Stop All Audio"; - hrstop.addEventListener("click", e => { - this.stopAudioInterval(); - }); - var hrhr = ce("hr") - hrtd1.id = "audioMarker"; - hrtd1.setAttribute("colspan", "3"); - hrtd1.append(hrstop, hrhr); - hrrow.append(hrtd1, hrtd2); - - // finally add rows to table - table.append(row, dropRow, hrrow); - } - - // create actual settings wrapper - var settings = ce("tr"); - settings.classList.add("show"); - settings.innerHTML = "

    Settings

    "; - table.append(settings); - - // process languages? - var local = proj.general.localization; - if (local) { - // set default language - if (!lang) lang = DefLang; - // add html struct - var row = ce("tr"); - row.classList.add("show"); - var td1 = ce("td"); - td1.innerHTML = "

    🇩🇪🇬🇧🇮🇹🇷🇺🇨🇳

    "; - var td2 = ce("td"); - var lan = ce("select"); - // process all - for (var loc in local) { - // build select option for this - var lcs = ce("option"); - lcs.value = loc; - lcs.innerHTML = loc.toUpperCase(); - lan.append(lcs); - // check for correct language code - if (loc != lang) continue; - else lcs.setAttribute("selected", "true"); - // set properties translated text - for (var p in props) { - var itm = props[p]; - var pTxt = itm.text; - var rTxt = local[loc][pTxt]; - if (rTxt) itm.realText = rTxt; - // process combo box values - if (itm.type == "combo") { - for (var o of itm.options) { - var lTxt = local[loc][o.label]; - if (lTxt) o.realLabel = lTxt; - } - } - } - } - // if changed, do it all over again. - const self = this; - lan.addEventListener("change", function (e) { - localStorage.setItem("wewwaLang", this.value); - self.AddMenu(this.value); - self.EvaluateSettings(); - (self.htmlIcon as any).click(); - }); - td2.setAttribute("colspan", "2"); - td2.append(lan); - row.append(td1, td2); - table.append(row); - } - - // split content from actual settings - var splitr = ce("tr"); - splitr.classList.add("show"); - splitr.innerHTML = "
    "; - table.append(splitr); - - // sort settings by order - var sortable = []; - for (var p in props) sortable.push([p, props[p]]); - sortable.sort((a, b) => a[1].order - b[1].order); - // add setting html elements - for (var s of sortable) - table.append(this.CreateItem(s[0], s[1])); - - // pre-footer for resetting saved settings - var preFoot = ce("div"); - preFoot.innerHTML = "

    "; - var reset = ce("a"); - reset.classList.add("red") - reset.innerHTML = "Reset Settings"; - reset.addEventListener("click", e => { - if (!window.confirm("This action will clear ALL local data!\r\n\r\nAre you sure?")) return; - OfflineHelper.Reset().then(() => { - localStorage.clear(); - location = location; - }); - }); - preFoot.append(reset); - - // footer with ident - var footer = ce("div"); - footer.innerHTML = "

    [W] allpaper
    [E] ngine
    [W] eb
    [W] allpaper
    [A] dapterby hexxone"; - // finish up menu - menu.append(preview, header, link, tmain, preFoot, footer); - - // create icon for opening & closing the menu - var icon = ce("div"); - icon.id = "wewwaIcon"; - icon.addEventListener("click", () => { - if(menu.classList.contains("open")) menu.classList.remove("open"); - else menu.classList.add("open"); - if(icon.classList.contains("open")) icon.classList.remove("open"); - else icon.classList.add("open"); - }); - var bar1 = ce("div"); - var bar2 = ce("div"); - var bar3 = ce("div"); - icon.append(bar1, bar2, bar3); - - // finally add the menu to the DOM - document.body.append(menu, icon); - this.htmlMenu = menu; - this.htmlIcon = icon; - } - - private CreateItem(prop, itm) { - if (!itm.type || itm.type == "hidden") return; - var self = this; - const ce = (e) => document.createElement(e); - var row = ce("tr"); - row.setAttribute("id", "wewwa_" + prop); - var td1 = ce("td"); - var td2 = ce("td"); - var td3 = null; - var txt = ce("div"); - txt.innerHTML = itm.realText ? itm.realText : itm.text; - // create real input element - var inp = ce("input"); - inp.setAttribute("id", "wewwa_inp_" + prop); - switch (itm.type) { - // only have text - case "text": - inp = null; - td1.setAttribute("colspan", 3); - break; - // add combo select options - case "combo": - inp = ce("select"); - // set options - for (var o of itm.options) { - var opt = ce("option"); - opt.setAttribute("value", o.value); - opt.innerText = o.realLabel ? o.realLabel : o.label; - if (itm.value == o.value) opt.setAttribute("selected", true); - inp.appendChild(opt); - } - break; - // browser color picker - case "color": - inp.setAttribute("type", "color"); - break; - // Checkbox - case "bool": - inp.setAttribute("type", "checkbox"); - inp.setAttribute("readonly", true); - // makes ticking checkboxes easier - row.addEventListener("click", (e) => { - inp.click(); - }); - break; - // Slider input - case "slider": - var canEdit = itm.editable; - // create numeric-up-down - var sliderVal = ce(canEdit ? "input" : "output"); - sliderVal.name = "wewwa_out_" + prop; - sliderVal.setAttribute("id", sliderVal.name); - sliderVal.setAttribute("type", "number"); - sliderVal.style.width = "75%"; - if (canEdit) { - sliderVal.setAttribute("value", itm.value); - sliderVal.addEventListener("change", function (e) { self.SetProperty(prop, this); }); - } - else { - sliderVal.innerHTML = itm.value; - } - // create td3 - td3 = ce("td"); - td3.append(sliderVal); - // create actual slider & values - inp.setAttribute("type", "range"); - inp.style.width = "100%"; - inp.max = itm.max; - inp.min = itm.min; - inp.step = 0.1; - break; - case "textinput": - inp.setAttribute("type", "text"); - break; - case "file": - inp.setAttribute("type", "file"); - break; - default: - Smallog.Error("unkown setting type: " + itm.type, LogHead); - break; - } - td1.append(txt); - // listen for changes if input type (no text) - if (inp) { - inp.addEventListener("change", function (e) { self.SetProperty(prop, this); }); - td2.prepend(inp); - } - // append td3 or stretch td2? - if (td3) { - row.append(td1, td2, td3) - } - else { - td2.setAttribute("colspan", 2); - row.append(td1, td2); - } - return row; - } - - - public SetProperty(prop, elm) { - - // get the type and apply the value - var props = this.project.general.properties; - - // check for legit setting... - if (!props[prop]) { - Smallog.Error("SetProperty name not found: " + prop, LogHead); - return; - } - - // enabled delayed call of settings update - var applyCall = (val) => { - // save the updated value to storage - props[prop].value = val; - // update - this.EvaluateSettings(); - var obj = {}; - obj[prop] = props[prop]; - this.ApplyProp(obj); - }; - - // process value based on DOM element type - switch (props[prop].type) { - case "bool": - applyCall(elm.checked == true); - break; - case "color": - applyCall(this.hexToRgb(elm.value)); - break; - case "file": - this.XHRLoadAndSaveLocal(elm.value, res => applyCall(res)); - break; - case "slider": - if (elm.name.includes("_out_")) { - var inpt: any = document.querySelector("#wewwa_" + prop); - if (inpt) inpt.value = elm.value; - else Smallog.Error("Slider not found: " + prop, LogHead); - } - else { - var slide: any = document.querySelector("#wewwa_out_" + prop); - if (slide) slide.value = elm.value; - else Smallog.Error("Numericupdown not found: " + prop, LogHead); - } - case "combo": - case "textinput": - applyCall(elm.value); - break; - } - } - - // will load the given file and return it as dataURL. - // this way we can easily store whole files in the configuration & localStorage. - // its not safe that this works with something else than image files. - private XHRLoadAndSaveLocal(url, resCall) { - myFetch(url, "blob").then(resp => { - // Read out file contents as a Data URL - const fReader = new FileReader(); - // onload needed since Google Chrome doesn't support addEventListener for FileReader - fReader.onload = (evt) => resCall(evt.target.result) - // Load blob as Data URL - fReader.readAsDataURL(resp); - }); - } - - - public EvaluateSettings() { - var wewwaProps = this.project.general.properties; - localStorage.setItem("wewwaLastProps", JSON.stringify(wewwaProps)); - for (var p in wewwaProps) { - var prop = wewwaProps[p]; - - // some ev(a|i)l magic - var visible = true; - if (prop.condition != null) { - // copy our condition string to modify - var cprop = String(prop.condition).split(" ").join(""); - // remove whitespaces and split to partials by logic operators - var partials = cprop.split(/&&|\|\|/); - // loop all partial values of the check - for (var part of partials) { - var prefix = "wewwaProps."; - var onlyVal = part.match(/[!a-zA-Z0-9_\.]*/)[0]; - if (!onlyVal.startsWith(prefix) && !onlyVal.startsWith("!" + prefix)) { - // fix for inverted values - var replW = onlyVal; - if (replW.startsWith("!")) { - replW = replW.substr(1); - prefix = "!" + prefix; - } - //Smallog.Debug("replace: " + onlyVal + " >> " + prefix + replW); - cprop = cprop.replace(onlyVal, prefix + replW); - } - } - try { - visible = eval(cprop) == true; - } - catch (e) { - Smallog.Error("Error: (" + cprop + ") for: " + p + " => " + e, LogHead); - } - } - - // get input dom element - const htElm = document.getElementById("wewwa_" + p); - if (!htElm || htElm.childNodes.length < 2) continue; - - if (visible) htElm.classList.add("show"); - else htElm.classList.remove("show"); - - // set its value - const elm: any = htElm.childNodes[1].childNodes[0]; - switch (prop.type) { - case "color": - elm.value = this.rgbToHex(prop.value); - break; - case "bool": - elm.checked = prop.value == true; - break; - case "slider": - case "combo": - case "textinput": - elm.value = prop.value; - break; - } - } - } - - public ApplyProp(prop) { - var wpl = window['wallpaperPropertyListener']; - if (wpl && wpl.applyUserProperties) { - wpl.applyUserProperties(prop); - } - } - - public SetPaused(val: boolean) { - var wpl = window['wallpaperPropertyListener']; - if (wpl && wpl.setPaused) { - wpl.setPaused(val); - } - } - - private rgbToHex(rgb) { - function cth(c) { - var h = Math.floor(c * 255).toString(16); - return h.length == 1 ? "0" + h : h; - } - var spl = rgb.split(" "); - return "#" + cth(spl[0]) + cth(spl[1]) + cth(spl[2]); - } - - private hexToRgb(hex) { - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255].join(" ") : null; - } - - private requestMicrophone() { - navigator.mediaDevices.getUserMedia({ - audio: true - }).then((stream) => { - this.stopAudioInterval(); - // hack for firefox to keep stream running - window['persistAudioStream'] = stream; - this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); - this.source = this.ctx.createMediaStreamSource(stream); - this.analyser = this.ctx.createAnalyser(); - this.analyser.smoothingTimeConstant = 0.35; - this.analyser.fftSize = 256; - - this.source.connect(this.analyser); - this.startAudioInterval(); - }).catch((err) => { - Smallog.Error(err, LogHead); - if (location.protocol != "https:") { - var r = confirm("Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS for this feature to work! Press 'ok'/'yes' to get redirected to HTTPS and try again."); - if (r) window.location.href = window.location.href.replace("http", "https"); - } - }); - } - - // html5 audio analyser gives us mono data from 0(bass) to 128(treble) - // however, wallpaper engine expects stereo data in following format: - // 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) - // so we do some array transformation... and divide by 255 (8bit-uint becomes float) - private convertAudio(data) { - var stereo = []; - var sIdx = 0; - for (var i = 0; i < 64; i++) { - stereo[i] = data[sIdx++] / 255; - stereo[127 - i] = data[sIdx++] / 255; - } - return stereo; - } - - - private initiateAudio(data) { - // clear up - this.stopAudioInterval(); - // create player - this.audio = document.createElement("audio"); - this.audio.src = data.name ? URL.createObjectURL(data) : data; - this.audio.autoplay = true; - this.audio.setAttribute("controls", "true"); - this.audio.play(); - - // insert before marker - const markr = document.getElementById("audioMarker"); - markr.parentElement.insertBefore(this.audio, markr); - - this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); - this.source = this.ctx.createMediaElementSource(this.audio); - this.analyser = this.ctx.createAnalyser(); - this.analyser.smoothingTimeConstant = 0.35; - this.analyser.fftSize = 256; - - this.source.connect(this.ctx.destination); - this.source.connect(this.analyser); - this.startAudioInterval(); - } - - - private startAudioInterval() { - var data = new Uint8Array(128); - // 33ms ~~ 30fps - this.audioInterval = setInterval(() => { - if (this.audioCallback == null) { - this.stopAudioInterval(); - Smallog.Error("no AudioCallback!", LogHead); - return; - } - this.analyser.getByteFrequencyData(data); - var stereo = this.convertAudio(data); - this.audioCallback(stereo); - }, 33); - // tell Wallpaper we are sending audio - this.ApplyProp({ audioprocessing: { value: true } }) - } - - public stopAudioInterval() { - window['persistAudioStream'] = null; - document.getElementById("wewwaAudioInput").setAttribute("value", ""); - if (this.audio) - this.audio.remove(); - if (this.audioInterval) { - clearInterval(this.audioInterval); - this.audioInterval = null; - } - } -} diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index dbcb4f1..595ed0a 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -1,117 +1,145 @@ /** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * Displays a seizure warning image centered on html for a given Time. - * - * @todo - * - add trigger warn languages to project json - * - add trigger warn as html - */ +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +* +* @description +* Displays a seizure warning image centered on html for a given Time. +* +* @todo +* - add trigger warn languages to project json +* - add trigger warn as html +*/ + +import {CComponent} from './CComponent'; +import {CSettings} from './CSettings'; +import {waitReady} from './Util'; + +const ELM_ID = 'triggerwarn'; -import { CComponent } from "./CComponent"; -import { CSettings } from "./CSettings"; -import { Ready } from "./Util"; - -const ELM_ID = "triggerwarn"; - -// TODO test getting text +/** +* @TODO test getting text +*/ class WarnSettings extends CSettings { - seizure_warning: boolean = true; - seizure_text: string = "/* */"; - animate_seconds: number = 1; - wait_seconds: number = 10; + seizure_warning: boolean = true; + seizure_text: string = '/* */'; + animate_seconds: number = 1; + wait_seconds: number = 10; } +/** +* Seizure warning display +*/ export class WarnHelper extends CComponent { - - public settings: WarnSettings = new WarnSettings(); - - private element: HTMLDivElement; - - // promise behind showing the warning - private showResolve: any; - - constructor() { - super(); - - Ready().then(() => { - this.injectCSS(); - this.injectHTML(); - }); - } - - private injectCSS() { - var st = document.createElement("style"); - st.innerHTML = ` - #${ELM_ID} { - object-fit: contain; - text-align: center; - max-height: 30vmax; - top: 25vmin; - opacity: 0; - transition: opacity ${this.settings.animate_seconds}s ease; - } - #${ELM_ID}.show { - opacity: 1; - } - `; - document.head.append(st); - } - - private injectHTML() { - this.element = document.createElement("div"); - this.element.id = ELM_ID; - document.body.append(this.element); - } - - private setText() { - this.element.innerHTML = this.settings.seizure_text.replace("\r\n", "
    "); - } - - public Show(): Promise { - return new Promise(resolve => { - // dont show - if (!this.settings.seizure_warning) { - resolve(); - return; - } - // wait for resolve by "Hide()" - this.showResolve = resolve; - // make text - this.setText(); - // show it - this.element.classList.add("show"); - // wait some time - setTimeout(this.Hide, this.settings.wait_seconds * 1000); - }); - } - - public Hide(): Promise { - return new Promise(resolve => { - // hide it & wait - this.element.classList.remove("show"); - setTimeout(() => { - if(this.showResolve) this.showResolve(); - this.showResolve = null; - resolve(); - }, this.settings.animate_seconds * 1000); - - }); - } - - public UpdateSettings(): Promise { - // update text - this.setText(); - // fix for instantly removing the warning while it shows - if(!this.settings.seizure_warning && this.element.classList.contains("show")) - return this.Hide(); - // whatever - return; - } -}; \ No newline at end of file + public settings: WarnSettings = new WarnSettings(); + + private element: HTMLDivElement; + + // promise behind showing the warning + private showResolve: any; + + /** + * Create and prepare once document ready + */ + constructor() { + super(); + + waitReady().then(() => { + this.injectCSS(); + this.injectHTML(); + }); + } + + /** + * Make custom style + */ + private injectCSS() { + const st = document.createElement('style'); + st.innerHTML = ` + #${ELM_ID} { + object-fit: contain; + text-align: center; + max-height: 30vmax; + top: 25vmin; + opacity: 0; + transition: opacity ${this.settings.animate_seconds}s ease; + } + #${ELM_ID}.show { + opacity: 1; + } + `; + document.head.append(st); + } + + /** + * Make custom html + */ + private injectHTML() { + this.element = document.createElement('div'); + this.element.id = ELM_ID; + document.body.append(this.element); + } + + /** + * Set html text value + */ + private setText() { + this.element.innerHTML = this.settings.seizure_text.replace('\r\n', '
    '); + } + + /** + * Show the warning + * @return {Promise} hidden again + */ + public show(): Promise { + return new Promise((resolve) => { + // dont show + if (!this.settings.seizure_warning) { + resolve(); + return; + } + // wait for resolve by "Hide()" + this.showResolve = resolve; + // make text + this.setText(); + // show it + this.element.classList.add('show'); + // wait some time + setTimeout(this.hide, this.settings.wait_seconds * 1000); + }); + } + + /** + * Hide warning + * @return {Promise} hidden + */ + public hide(): Promise { + return new Promise((resolve) => { + // hide it & wait + this.element.classList.remove('show'); + setTimeout(() => { + if (this.showResolve) this.showResolve(); + this.showResolve = null; + resolve(); + }, this.settings.animate_seconds * 1000); + }); + } + + /** + * Settings have been changed + * @return {Promise} finished + */ + public updateSettings(): Promise { + // update text + this.setText(); + // fix for instantly removing the warning while it shows + if (!this.settings.seizure_warning && this.element.classList.contains('show')) { + return this.hide(); + } + // whatever + return; + } +}; diff --git a/src/offline/Offline.ts b/src/offline/Offline.ts new file mode 100644 index 0000000..e6b9022 --- /dev/null +++ b/src/offline/Offline.ts @@ -0,0 +1,165 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +* +* @description +* Generic Service Worker for Caching an app and making it available offline. +* Needs to be passed the `?jsonPath=` argument with a path to a file json. +* Everything else is explained in between... +* +* @see +* Read more: https://ponyfoo.com/articles/progressive-networking-serviceworker +*/ + +'use strict'; + +const wrk: ServiceWorker = self as any; + +// A version number is useful when updating the worker logic, +// allowing you to remove outdated cache entries during the update. +const wName = '[OfflineWorker]'; +const version = '::2.4'; +console.info(wName + 'executing.'); + +// The install event fires when the service worker is first installed. +// You can use this event to prepare the service worker to be able to serve +// files while visitors are offline. +wrk.addEventListener('install', function(event: any) { + console.info(wName + ' install event in progress.'); + + // get the path for the .json file which contains the files to-be-cached + // These resources will be downloaded and cached by the service worker + // If any resource fails to be downloaded, then the service worker won't be installed. + const sp = new URL(location.href).searchParams; + const jsonPath = sp.get('jsonPath'); + + // Using event.waitUntil(p) blocks the installation process on the provided + // promise. If the promise is rejected, the service worker won't be installed. + event.waitUntil( + // let's request the files to store in cache + fetch(jsonPath) + .then((response) => response.json()) + // after we received the files to store, we open the cache + .then((data) => caches.open(wName + version) + // then map the array and add one-by-one + .then((cache) => data.map((url) => cache.add(url)))) + // log success + .then(() => console.info(wName + ' install completed')), + ); +}); + +// The fetch event fires whenever a page controlled by this service worker requests +// a resource. This isn't limited to `fetch` or even XMLHttpRequest. Instead, it +// comprehends even the request for the HTML page on first load, as well as JS and +// CSS resources, fonts, any images, etc. +wrk.addEventListener('fetch', function(event: any) { + // console.info(wName + 'fetch event in progress.'); + + if (event.request.method !== 'GET') { + console.info(wName + ' fetch event ignored.', event.request.method, event.request.url); + return; + } + + // This method returns a promise that resolves to a cache entry matching + // the request. Once the promise is settled, we can then provide a response + // to the fetch request. + event.respondWith( + caches.match(event.request) + .then(async (cached) => { + // return immediately if cache successfull + if (cached) { + console.info(wName + ' fetch event(cached): ', event.request.url); + return cached; + } + + // Else, use the preloaded response, if it's there + const preload = await event.preloadResponse; + if (preload) return preload; + + /** + * We copy the response before replying to the network request. + * @param {Response} response This will be stored on the ServiceWorker cache. + * @return {Response} cached + */ + function fetchedFromNetwork(response: Response) { + const cacheCopy = response.clone(); + console.info(wName + ' fetch response from network.', event.request.url); + // We open a cache to store the response for this request. + caches.open(wName + version) + .then((cache) => cache.put(event.request, cacheCopy)) + .then(() => console.info(wName + ' fetch response stored in cache.', event.request.url)); + // Return the response so that the promise is settled in fulfillment. + return response; + } + + /** + * When this method is called, it means we were unable to produce a response + * from either the cache or the network. This is our last opportunity + * to produce a meaningful response even when all else fails. + * E.g. + * - Test the Accept header and then return one of the `offlineFundamentals` + * e.g: `return caches.match('/some/cached/image.png')` + * - consider the origin. It's easier to decide what "unavailable" means + * for requests against your origins than for requests against a third party, + * such as an ad provider. + * - Generate a Response programmaticaly, as shown below, and return that. + * @return {Response} + */ + function unableToResolve() { + console.info(wName + ' fetch request failed in both cache and network.'); + return new Response('Service Unavailable', { + status: 503, + statusText: 'Service Unavailable', + headers: new Headers({ + 'Content-Type': 'text/html', + }), + }); + } + + // fallback to fetching from network + return fetch(event.request) + // We handle the network request with success and failure scenarios. + .then(fetchedFromNetwork, unableToResolve) + // we are done + .then(() => console.info(wName + ' fetch event(networked): ', event.request.url)) + // We should catch errors on the fetchedFromNetwork handler as well. + .catch(unableToResolve); + }), + ); +}); + +/** + * The activate event fires after a service worker has been successfully installed. + * It is most useful when phasing out an older version of a service worker, as at + * this point you know that the new worker was installed correctly. In this example, + * we delete old caches that don't match the version in the worker we just finished + * installing. +*/ +wrk.addEventListener('activate', function(event: any) { + console.info(wName + ' activate event in progress.'); + event.waitUntil(async () => { + // Feature-detect navigation preloads! + if (self['registration'] && self['registration'].navigationPreload) { + await self['registration'].navigationPreload.enable(); + } + + // This method will resolve an array of available cache keys. + caches.keys() + // We return a promise that settles when all outdated caches are deleted. + .then((keys) => Promise.all( + // Filter by keys that don't start with the latest version prefix. + keys.filter((key) => !key.endsWith(version)) + // Return a promise that's fulfilled when each outdated cache is deleted. + .map((key) => caches.delete(key)))) + // completed + .then(() => console.info(wName + ' activate completed.')); + }); + + // Tell the active service worker to take control of the page immediately. + if (wrk['clients']) wrk['clients'].claim(); +}); + diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index b405f81..01ed727 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -1,68 +1,81 @@ /** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * Helper class for loading and registering the ServiceWorker - * - * the workerPath is the path to the compiled OfflineWorker js file, relative from the calling html file... - * the "?jsonPath=" argument will give the worker a "public available" json file, consisting of a string array. - * the array will contain all necessary files to run the app offline. - * the ServiceWorker will cache these files and automatically load them if the website is ran offline. - * ServiceWorker is a progressive technology. Some browsers will be unsupported... - */ +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ +/* eslint-disable no-unused-vars */ -import { Smallog } from "../Smallog"; +import {Smallog} from '../Smallog'; // this should pack the serviceworker like a webwoker. -import OfflineWorker from 'worker-loader!./OfflineWorker'; +import OfflineWorker from 'worker-loader!./Offline'; -const LogHead = "[OfflineHelper] "; -const LogErrS = "service-worker is not supported."; +const LogHead = '[OfflineHelper] '; +const LogErrS = 'service-worker is not supported.'; +/** +* @ignore +* Helper class for loading and registering the ServiceWorker +*
    +* the workerPath is the path to the compiled OfflineWorker js file, relative from the calling html file... +*
    +* the "?jsonPath=" argument will give the worker a "public available" json file, consisting of a string array. +*
    +* the array will contain all necessary files to run the app offline. +*
    +* the ServiceWorker will cache these files and automatically load them if the website is ran offline. +*
    +* ServiceWorker is a progressive technology. Some browsers will be unsupported... +*/ export module OfflineHelper { - // function helper, so OfflineWorker is actually processed - function DontRemove() { return new OfflineWorker() }; + // function helper, so OfflineWorker is actually processed + // eslint-disable-next-line require-jsdoc + function DontRemove() { + return new OfflineWorker(); + }; - // In order to intercept ALL fetch-requests offline, the scope "/" (root) is required. - // when you put in in a sub-directory like "/js/", the scope is also "/js/". - // Then, your HTTP Server will have to send the REPONSE HEADER `service-worker-allowed: /` - // otherwise it will cause an ERROR in your Browser. So: Putting the ServiceWorker in root folder is easiest. - // obviously with webpack, this causes a problem, when you are not outputting directly into the root dir... - export function Register(workerPath: string = "OfflineWorker.worker.js?jsonPath=offlinefiles.json") { - return new Promise(async (resolve) => { - if ('serviceWorker' in navigator) { - await navigator.serviceWorker.register(workerPath, { scope: "/" }) - .then(() => Smallog.Info('service-worker registration complete.', LogHead), - () => Smallog.Error('service-worker registration failure.', LogHead)) - .then(() => resolve(true)); - return true; - } - else { - Smallog.Error(LogErrS, LogHead); - resolve(false); - } - }); - } + // In order to intercept ALL fetch-requests offline, the scope "/" (root) is required. + // when you put in in a sub-directory like "/js/", the scope is also "/js/". + // Then, your HTTP Server will have to send the REPONSE HEADER `service-worker-allowed: /` + // otherwise it will cause an ERROR in your Browser. So: Putting the ServiceWorker in root folder is easiest. + // obviously with webpack, this causes a problem, when you are not outputting directly into the root dir... + // eslint-disable-next-line require-jsdoc + export function register(name: string, worker = 'Offline.worker.js', oFile = 'offlinefiles.json') { + return new Promise(async (resolve) => { + if ('serviceWorker' in navigator) { + const workerPath = `${worker}?name=${name}&jsonPath=${oFile}`; + await navigator.serviceWorker.register(workerPath, {scope: '/'}) + .then(() => Smallog.info('service-worker registration complete.', LogHead), + () => Smallog.Error('service-worker registration failure.', LogHead)) + .then(() => resolve(true)); + return true; + } else { + Smallog.Error(LogErrS, LogHead); + resolve(false); + } + }); + } - // unregister all service workers - export async function Reset() { - return new Promise((resolve) => { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.getRegistrations().then(async registrations => { - for (let registration of registrations) - await registration.unregister(); - resolve(true); - }); - } - else { - Smallog.Error(LogErrS, LogHead); - resolve(false); - } - }); - } -} \ No newline at end of file + /** + * unregister all service workers + * @return {Promise} finished + */ + export async function reset() { + return new Promise((resolve) => { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.getRegistrations().then(async (registrations) => { + for (const registration of registrations) { + await registration.unregister(); + } + resolve(true); + }); + } else { + Smallog.Error(LogErrS, LogHead); + resolve(false); + } + }); + } +} diff --git a/src/offline/OfflinePlugin.js b/src/offline/OfflinePlugin.js index d0365f7..619ebf4 100644 --- a/src/offline/OfflinePlugin.js +++ b/src/offline/OfflinePlugin.js @@ -1,123 +1,150 @@ /** - * - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * This is a webpack plugin for easily making your webapp available Offline. - * It will simply output a json list of files to cache for the Service Worker on build. - * - * In your source, you just 'require' and 'Register()' the OfflineHelper. - * The OfflineHelper will then require the OfflineWorker in background. - * - * The OfflineWorker will then: - * 1. launch itself - * 2. load the 'offlinefiles.json' list - * 3. cache all files in it - * 4. return cached files if network fetching fails - * - * @see - * You can also ignore this plugin and create the 'offlinefiles.json' yourself. - */ +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +* @ignore +*/ -const fs = require("fs"); +const fs = require('fs'); const validate = require('schema-utils'); -const { Compilation } = require("webpack"); -const { RawSource } = require('webpack-sources'); +const {Compilation} = require('webpack'); +const {RawSource} = require('webpack-sources'); const pluginName = 'OfflinePlugin'; -// schema for options object -const schema = { - type: 'object', - properties: { - staticdir: { - type: 'string' - }, - outfile: { - type: 'string' - }, - extrafiles: { - type: 'array' - }, - pretty: { - type: 'boolean' - } - } +/** + * schema for options object + * @see {OfflinePlugin} + */ +const offliineSchema = { + type: 'object', + properties: { + staticdir: { + type: 'string', + }, + outfile: { + type: 'string', + }, + extrafiles: { + type: 'array', + }, + pretty: { + type: 'boolean', + }, + }, }; -// list files recursively +/** +* list files recursively +* @param {strring} baseDir start directory +* @param {string} subDir sub directory +* @param {array} arrayOfFiles result files +* @return {array} arrayOfFiles +*/ function getAllFiles(baseDir, subDir, arrayOfFiles) { - var sub = baseDir + "/" + subDir; - var files = fs.readdirSync(sub); - var arrayOfFiles = arrayOfFiles || []; - files.forEach((file) => { - var fle = sub + "/" + file; - if (fs.statSync(fle).isDirectory()) - arrayOfFiles = getAllFiles(baseDir, subDir + "/" + file, arrayOfFiles); - else - arrayOfFiles.push(subDir + "/" + file); - }); - return arrayOfFiles; + const sub = baseDir + '/' + subDir; + const files = fs.readdirSync(sub); + arrayOfFiles = arrayOfFiles || []; + files.forEach((file) => { + const fle = sub + '/' + file; + if (fs.statSync(fle).isDirectory()) { + arrayOfFiles = getAllFiles(baseDir, subDir + '/' + file, arrayOfFiles); + } else { + arrayOfFiles.push(subDir + '/' + file); + } + }); + return arrayOfFiles; } -// actual webpack plugin +/** +* This is a webpack plugin for easily making your webapp available Offline. +*
    +* It will simply output a json list of files to cache for the Service Worker on build. +*
    +* In your source, you just 'require' and 'Register()' the OfflineHelper. +*
    +* The OfflineHelper will then require the OfflineWorker in background. +*
    +*
    +* +* The OfflineWorker will then: +*
    +* 1. launch itself +*
    +* 2. load the 'offlinefiles.json' list +*
    +* 3. cache all files in it +*
    +* 4. return cached files if network fetching fails +*
    +*
    +* You can also ignore this plugin and create the 'offlinefiles.json' yourself. +*/ class OfflinePlugin { - - options = {}; - - constructor(options = {}) { - validate.validate(schema, options); - this.options = options; - } - - // Define `apply` as its prototype method which is supplied with compiler as its argument - apply(compiler) { - var addedOnce = false; - // Specify the event hook to attach to - compiler.hooks.thisCompilation.tap(pluginName, (compilation) => - compilation.hooks.processAssets.tap({ - name: pluginName, - stage: Compilation.PROCESS_ASSETS_STAGE_ANALYSE, - }, () => { - if (addedOnce) return; - addedOnce = true; - - console.info("[" + pluginName + "] Gathering Infos..."); - - // list of all app-contents - var filelist = []; - - // add static files from folder - var sFiles = getAllFiles(this.options.staticdir, ""); - for (var staticFile in sFiles) { - filelist.push(sFiles[staticFile]); - } - - // Loop through all compiled assets, - // adding a new line item for each filename. - for (var filename in compilation.assets) { - filelist.push('/' + filename); - } - - // add additional files anyway? - if (this.options.extrafiles) - this.options.extrafiles.map(ef => filelist.push(ef)); - - // create the target file with all app-contents as json list - const jList = JSON.stringify(filelist, null, this.options.pretty ? 1 : 0); - compilation.emitAsset(this.options.outfile, new RawSource(jList)); - console.info("[" + pluginName + "] result: " + jList); - - console.info("[" + pluginName + "] finished."); - } - )); - } + options = {}; + + /** + * Intializes the plugin in the webpack build process + * @param {offliineSchema} options + */ + constructor(options = {}) { + validate.validate(offliineSchema, options); + this.options = options; + } + + /** + * Hook into the compilation process, + * find all target files and put them into a json file + * @param {Webpack.compiler} compiler object from webpack + */ + apply(compiler) { + let addedOnce = false; + // Specify the event hook to attach to + compiler.hooks.thisCompilation.tap(pluginName, (compilation) => + compilation.hooks.processAssets.tap({ + name: pluginName, + stage: Compilation.PROCESS_ASSETS_STAGE_ANALYSE, + }, () => { + if (addedOnce) return; + addedOnce = true; + + console.info('[' + pluginName + '] Gathering Infos...'); + + // list of all app-contents + const filelist = []; + + // add static files from folder + const sFiles = getAllFiles(this.options.staticdir, ''); + for (const staticFile in sFiles) { + if (!staticFile) continue; + filelist.push(sFiles[staticFile]); + } + + // Loop through all compiled assets, + // adding a new line item for each filename. + for (const filename in compilation.assets) { + if (!filename) continue; + filelist.push('/' + filename); + } + + // add additional files anyway? + if (this.options.extrafiles) { + this.options.extrafiles.map((ef) => filelist.push(ef)); + } + + // create the target file with all app-contents as json list + const jList = JSON.stringify(filelist, null, this.options.pretty ? 1 : 0); + compilation.emitAsset(this.options.outfile, new RawSource(jList)); + console.info('[' + pluginName + '] result: ' + jList); + + console.info('[' + pluginName + '] finished.'); + }, + )); + } } -module.exports = OfflinePlugin; \ No newline at end of file +module.exports = OfflinePlugin; diff --git a/src/offline/OfflineWorker.ts b/src/offline/OfflineWorker.ts deleted file mode 100644 index 71eaf62..0000000 --- a/src/offline/OfflineWorker.ts +++ /dev/null @@ -1,159 +0,0 @@ -/** - * - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * Generic Service Worker for Caching an app and making it available offline. - * Needs to be passed the `?jsonPath=` argument with a path to a file json. - * Everything else is explained in between... - * - * @see - * Read more: https://ponyfoo.com/articles/progressive-networking-serviceworker - */ - -"use strict"; - -const wrk: ServiceWorker = self as any; - -// A version number is useful when updating the worker logic, -// allowing you to remove outdated cache entries during the update. -const version = 'v2.4::'; -const wName = "[OfflineWorker] "; - -console.info(wName + 'executing.'); - -// The install event fires when the service worker is first installed. -// You can use this event to prepare the service worker to be able to serve -// files while visitors are offline. -wrk.addEventListener("install", function (event: any) { - console.info(wName + 'install event in progress.'); - - // get the path for the .json file which contains the files to-be-cached - // These resources will be downloaded and cached by the service worker - // If any resource fails to be downloaded, then the service worker won't be installed. - var jsonPath = new URL(location.href).searchParams.get('jsonPath'); - - // Using event.waitUntil(p) blocks the installation process on the provided - // promise. If the promise is rejected, the service worker won't be installed. - event.waitUntil( - // let's request the files to store in cache - fetch(jsonPath) - .then(response => response.json()) - // after we received the files to store, we open the cache - .then(data => caches.open(version + 'fundamentals') - // then map the array and add one-by-one - .then(cache => data.map((url) => cache.add(url)))) - // log success - .then(() => console.info(wName + 'install completed')) - ); -}); - -// The fetch event fires whenever a page controlled by this service worker requests -// a resource. This isn't limited to `fetch` or even XMLHttpRequest. Instead, it -// comprehends even the request for the HTML page on first load, as well as JS and -// CSS resources, fonts, any images, etc. -wrk.addEventListener("fetch", function (event: any) { - //console.info(wName + 'fetch event in progress.'); - - if (event.request.method !== 'GET') { - console.info(wName + 'fetch event ignored.', event.request.method, event.request.url); - return; - } - - // This method returns a promise that resolves to a cache entry matching - // the request. Once the promise is settled, we can then provide a response - // to the fetch request. - event.respondWith( - caches.match(event.request) - .then(async (cached) => { - // return immediately if cache successfull - if (cached) { - console.info(wName + 'fetch event(cached): ', event.request.url); - return cached - } - - // Else, use the preloaded response, if it's there - const preload = await event.preloadResponse; - if (preload) return preload; - - // We copy the response before replying to the network request. - // This is the response that will be stored on the ServiceWorker cache. - function fetchedFromNetwork(response) { - var cacheCopy = response.clone(); - console.info(wName + 'fetch response from network.', event.request.url); - // We open a cache to store the response for this request. - caches.open(version + 'pages') - .then((cache) => cache.put(event.request, cacheCopy)) - .then(() => console.info(wName + 'fetch response stored in cache.', event.request.url)); - // Return the response so that the promise is settled in fulfillment. - return response; - } - - /* When this method is called, it means we were unable to produce a response - from either the cache or the network. This is our last opportunity - to produce a meaningful response even when all else fails. - E.g. - - Test the Accept header and then return one of the `offlineFundamentals` - e.g: `return caches.match('/some/cached/image.png')` - - consider the origin. It's easier to decide what "unavailable" means - for requests against your origins than for requests against a third party, - such as an ad provider. - - Generate a Response programmaticaly, as shown below, and return that. - */ - function unableToResolve() { - console.info(wName + 'fetch request failed in both cache and network.'); - return new Response('Service Unavailable', { - status: 503, - statusText: 'Service Unavailable', - headers: new Headers({ - 'Content-Type': 'text/html' - }) - }); - } - - // fallback to fetching from network - return fetch(event.request) - // We handle the network request with success and failure scenarios. - .then(fetchedFromNetwork, unableToResolve) - // we are done - .then(() => console.info(wName + 'fetch event(networked): ', event.request.url)) - // We should catch errors on the fetchedFromNetwork handler as well. - .catch(unableToResolve); - }) - ); -}); - -/* The activate event fires after a service worker has been successfully installed. - It is most useful when phasing out an older version of a service worker, as at - this point you know that the new worker was installed correctly. In this example, - we delete old caches that don't match the version in the worker we just finished - installing. -*/ -wrk.addEventListener("activate", function (event: any) { - console.info(wName + 'activate event in progress.'); - event.waitUntil(async () => { - // Feature-detect navigation preloads! - if (self['registration'] && self['registration'].navigationPreload) { - await self['registration'].navigationPreload.enable(); - } - - // This method will resolve an array of available cache keys. - caches.keys() - // We return a promise that settles when all outdated caches are deleted. - .then(keys => Promise.all( - // Filter by keys that don't start with the latest version prefix. - keys.filter((key) => !key.startsWith(version)) - // Return a promise that's fulfilled when each outdated cache is deleted. - .map((key) => caches.delete(key)))) - // completed - .then(() => console.info(wName + 'activate completed.')); - }); - - // Tell the active service worker to take control of the page immediately. - if (wrk['clients']) wrk['clients'].claim(); -}); \ No newline at end of file diff --git a/src/wasc-worker b/src/wasc-worker index e8d9269..f7350c1 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit e8d9269fe3adf8d5fed3ba2a8bcd7b2543f71323 +Subproject commit f7350c1964b775d1dfb65469580da6deae78174f diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts deleted file mode 100644 index 8e1b807..0000000 --- a/src/weas/WEAS.ts +++ /dev/null @@ -1,216 +0,0 @@ -/** - * @author D.Thiele @https://hexx.one - * - * @license - * Copyright (c) 2020 D.Thiele All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - * - * @description - * WEWWA - * Wallpaper Engine Audio Supplier makes working with audio easier. - * It will automatically start to receive and process the audio data - * which can then be accessed on the global object. - * - * DEPENDS ON: - * - jQuery (window loaded event) - * - Wallpaper Engine Web Wallpaper environment - * - audio-processing supported web wallpaper... - * -*/ - -import { ASUtil } from "@assemblyscript/loader"; -import { CComponent } from "../CComponent"; -import { CSettings } from "../CSettings"; -import { Ready } from "../Util"; -import { Smallog } from "../Smallog"; - -import WascInit from '../wasc-worker'; - -const DAT_LEN = 128; - -export class WEASettings extends CSettings { - audioprocessing: boolean = true; - // do pink-noise processing? - equalize: boolean = true; - // convert to mono? - mono_audio: boolean = true; - // invert low & high freqs? - audio_direction: number = 0; - // peak filtering - peak_filter: number = 1; - // neighbour-smoothing value - value_smoothing: number = 2; - // time-value smoothing ratio - audio_increase: number = 75; - audio_decrease: number = 35; - // multipliers - treble_multiplier: number = 0.5; - mids_multiplier: number = 0.75; - bass_multiplier: number = 1.8; - // ignore value leveling for "silent" data - minimum_volume: number = 0.005; - // use low latency audio? - low_latency: boolean = false; -} - -enum Sett { - equalize = 0, - mono_audio = 1, - audio_direction = 2, - peak_filter = 3, - value_smoothing = 4, - audio_increase = 5, - audio_decrease = 6, - treble_multiplier = 7, - mids_multiplier = 8, - bass_multiplier = 9, - minimum_volume = 10, -} - -enum Props { - bass = 0, - mids = 1, - highs = 2, - sum = 3, - min = 4, - max = 5, - average = 6, - range = 7, - silent = 8, - intensity = 9 -} - -export class WEAS extends CComponent { - - // last processed audio object - public lastAudio = null; - - // settings object - public settings: WEASettings = new WEASettings(); - - // create transfer buffer - private inBuff = new Float64Array(DAT_LEN); - - // web assembly functions - private weasModule: any = null; - - constructor() { - super(); - // delay audio initialization - Ready().then(() => this.realInit()); - } - - private async realInit() { - // if wallpaper engine context given, listen - if (!window['wallpaperRegisterAudioListener']) { - Smallog.Info("'window.wallpaperRegisterAudioListener' not given!"); - return; - } - - var self = this; - - this.weasModule = await WascInit('WEAS.wasm', {}, !this.settings.low_latency); - const { run } = this.weasModule; - - // pass settings to module - await this.UpdateSettings(); - - // register audio callback on module - window['wallpaperRegisterAudioListener'](async audioArray => { - // check proof - if (!audioArray || !this.settings.audioprocessing) return; - if (audioArray.length != DAT_LEN) { - Smallog.Error("audioListener: received invalid audio data array. Length: " + audioArray.length); - return; - } - - // prepare data - const start = performance.now(); - this.inBuff.set(audioArray); - - // WRAP IN isolated Function ran inside worker - run(({ module, instance, importObject, params }) => { - const { exports } = instance; - const { data } = params[0]; - const io = importObject as ASUtil; - - // set audio data directly in module memory - io.__getFloat64ArrayView(exports.inputData).set(data); - // trigger processing processing - exports.update(); - // get copy of updated Data & Properties - return { - data: new Float64Array(io.__getFloat64ArrayView(exports.outputData)), - props: new Float64Array(io.__getFloat64ArrayView(exports.audioProps)), - } - }, { - // params passed to worker - data: this.inBuff - }).then((result) => { - // worker result, back in main context - const { data, props } = result; - // apply actual last Data from worker - self.lastAudio = { - time: start, - data, - ...this.getProps(props) - }; - // print info - Smallog.Debug("Got Data from Worker! Time= " + (performance.now() - start) + ", Data= " + JSON.stringify(data)); - }); - }); - } - - // converts calculated output property number-array to string-associative-array - private getProps(dProps: ArrayLike) { - var keys = Object.keys(Props); - keys = keys.slice(keys.length / 2); - var res = {}; - for (var index = 0; index < keys.length; index++) { - const key = keys[index]; - res[key] = dProps[Props[key]]; - } - return res; - } - - // CAVEAT: only available after init and module load - public UpdateSettings(): Promise { - if (!this.weasModule) return; - const { run } = this.weasModule; - - var keys = Object.keys(Sett); - keys = keys.slice(keys.length / 2); - const sett = new Float64Array(keys.length); - for (let index = 0; index < keys.length; index++) { - const key = keys[index]; - sett[Sett[key]] = this.settings[key] || 0; - } - - // WRAP IN isolated Function ran inside worker - return run(({ module, instance, importObject, params }) => { - const { exports } = instance; - const { data } = params[0]; - const io = importObject as ASUtil; - io.__getFloat64ArrayView(exports.audioSettings).set(data); - }, { - // Data passed to worker - data: sett - }).then(() => { - Smallog.Debug("Sent Settings to WEAS: " + JSON.stringify(sett)); - }); - } - - /** - * return false if: - * - processing is disabled - * - there is no data - * - the data is silent - * - data is too old (> 3 - */ - public hasAudio() { - return this.settings.audioprocessing - && this.lastAudio && this.lastAudio.silent == 0 - && (performance.now() / 1000 - this.lastAudio.time < 3); - } -} \ No newline at end of file diff --git a/src/worker-loader.d.ts b/src/worker-loader.d.ts index b61d94d..e76049c 100644 --- a/src/worker-loader.d.ts +++ b/src/worker-loader.d.ts @@ -1,14 +1,30 @@ -/* - * This is a definition wrapper, connecting the "compiled" workers to the actual worker loader. - */ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ +/** +* This is just a definition wrapper. +* +* @see https://github.com/webpack-contrib/worker-loader +*/ declare module 'worker-loader!*' { - // You need to change `Worker`, if you specified a different value for the `workerType` option - class WebpackWorker extends Worker { - constructor(options?: any); - } - // Uncomment this if you set the `esModule` option to `false` - //export = WebpackWorker; - export default WebpackWorker; -} \ No newline at end of file + /** + * You need to change `Worker`, if: + * you specified a different value for the `workerType` option + */ + class WebpackWorker extends Worker { + constructor(options?: any); + } + + /** + * Change this if you set the `esModule` option to `false` + * // export = WebpackWorker; + */ + export default WebpackWorker; +} From cb3d968e0a0a3fffa0385ddbfdd3914557a919b1 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Tue, 6 Apr 2021 22:42:56 +0200 Subject: [PATCH 29/76] update submodules, fix & clean docs, add workflow --- .github/workflows/main.yml | 24 + README.md | 8 +- docs/CComponent.html | 1828 +++++---- docs/CComponent.ts.html | 12 +- docs/CSettings.html | 924 ++--- docs/CSettings.ts.html | 4 +- docs/CUESettings.html | 2 +- docs/OfflinePlugin.html | 920 ++--- docs/ReloadHelper.html | 200 +- docs/ReloadHelper.ts.html | 4 +- docs/ReloadSettings.html | 2 +- docs/{Stats.ts.html => Smallog.ts.html} | 147 +- docs/Stats.html | 204 - docs/Util.ts.html | 6 +- docs/WEAS.html | 2173 +++++----- docs/WEAS.ts.html | 49 +- docs/WEASettings.html | 8 +- docs/WEICUE.html | 3334 +++++----------- docs/WEICUE.ts.html | 18 +- docs/WEWA.ts.html | 155 +- docs/WEWWA.html | 3701 ++++-------------- docs/WarnHelper.html | 2035 +++++----- docs/WarnHelper.ts.html | 7 +- docs/WarnSettings.html | 225 +- docs/WascBuilderPlugin.html | 435 -- docs/WascInterface.html | 251 -- docs/fonts/OpenSans-Bold-webfont.eot | Bin 19544 -> 0 bytes docs/fonts/OpenSans-Bold-webfont.svg | 1830 --------- docs/fonts/OpenSans-Bold-webfont.woff | Bin 22432 -> 0 bytes docs/fonts/OpenSans-BoldItalic-webfont.eot | Bin 20133 -> 0 bytes docs/fonts/OpenSans-BoldItalic-webfont.svg | 1830 --------- docs/fonts/OpenSans-BoldItalic-webfont.woff | Bin 23048 -> 0 bytes docs/fonts/OpenSans-Italic-webfont.eot | Bin 20265 -> 0 bytes docs/fonts/OpenSans-Italic-webfont.svg | 1830 --------- docs/fonts/OpenSans-Italic-webfont.woff | Bin 23188 -> 0 bytes docs/fonts/OpenSans-Light-webfont.eot | Bin 19514 -> 0 bytes docs/fonts/OpenSans-Light-webfont.svg | 1831 --------- docs/fonts/OpenSans-Light-webfont.woff | Bin 22248 -> 0 bytes docs/fonts/OpenSans-LightItalic-webfont.eot | Bin 20535 -> 0 bytes docs/fonts/OpenSans-LightItalic-webfont.svg | 1835 --------- docs/fonts/OpenSans-LightItalic-webfont.woff | Bin 23400 -> 0 bytes docs/fonts/OpenSans-Regular-webfont.eot | Bin 19836 -> 0 bytes docs/fonts/OpenSans-Regular-webfont.svg | 1831 --------- docs/fonts/OpenSans-Regular-webfont.woff | Bin 22660 -> 0 bytes docs/global.html | 410 +- docs/index.html | 8 +- docs/offline_OfflinePlugin.js.html | 6 +- docs/scripts/prettify/Apache-License-2.0.txt | 202 - docs/scripts/prettify/lang-css.js | 2 - docs/scripts/prettify/prettify.js | 28 - docs/styles/jsdoc-default.css | 358 -- docs/wasc-worker_WascBuilderPlugin.js.html | 322 -- docs/wasc-worker_WascInterface.ts.html | 136 - docs/wasc-worker_WascLoader.ts.html | 292 -- docs/wasc-worker_WascRT.ts.html | 277 -- docs/wasc-worker_WascWorker.ts.html | 147 - docs/wasc-worker_index.ts.html | 147 - jsdoc.json | 6 +- src/CComponent.ts | 10 +- src/CSettings.ts | 2 +- src/ReloadHelper.ts | 2 + src/Smallog.ts | 71 +- src/Util.ts | 4 +- src/WEAS.ts | 25 +- src/WEICUE.ts | 16 +- src/WEWA.ts | 153 +- src/WarnHelper.ts | 5 + src/offline/OfflineHelper.ts | 6 +- src/offline/OfflinePlugin.js | 4 +- src/wasc-worker | 2 +- 70 files changed, 6750 insertions(+), 23554 deletions(-) create mode 100644 .github/workflows/main.yml rename docs/{Stats.ts.html => Smallog.ts.html} (57%) delete mode 100644 docs/Stats.html delete mode 100644 docs/WascBuilderPlugin.html delete mode 100644 docs/WascInterface.html delete mode 100644 docs/fonts/OpenSans-Bold-webfont.eot delete mode 100644 docs/fonts/OpenSans-Bold-webfont.svg delete mode 100644 docs/fonts/OpenSans-Bold-webfont.woff delete mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.eot delete mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.svg delete mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.woff delete mode 100644 docs/fonts/OpenSans-Italic-webfont.eot delete mode 100644 docs/fonts/OpenSans-Italic-webfont.svg delete mode 100644 docs/fonts/OpenSans-Italic-webfont.woff delete mode 100644 docs/fonts/OpenSans-Light-webfont.eot delete mode 100644 docs/fonts/OpenSans-Light-webfont.svg delete mode 100644 docs/fonts/OpenSans-Light-webfont.woff delete mode 100644 docs/fonts/OpenSans-LightItalic-webfont.eot delete mode 100644 docs/fonts/OpenSans-LightItalic-webfont.svg delete mode 100644 docs/fonts/OpenSans-LightItalic-webfont.woff delete mode 100644 docs/fonts/OpenSans-Regular-webfont.eot delete mode 100644 docs/fonts/OpenSans-Regular-webfont.svg delete mode 100644 docs/fonts/OpenSans-Regular-webfont.woff delete mode 100644 docs/scripts/prettify/Apache-License-2.0.txt delete mode 100644 docs/scripts/prettify/lang-css.js delete mode 100644 docs/scripts/prettify/prettify.js delete mode 100644 docs/styles/jsdoc-default.css delete mode 100644 docs/wasc-worker_WascBuilderPlugin.js.html delete mode 100644 docs/wasc-worker_WascInterface.ts.html delete mode 100644 docs/wasc-worker_WascLoader.ts.html delete mode 100644 docs/wasc-worker_WascRT.ts.html delete mode 100644 docs/wasc-worker_WascWorker.ts.html delete mode 100644 docs/wasc-worker_index.ts.html diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..f511c40 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,24 @@ +name: Automatic JsDoc pages + +on: + push: + branches: + - custom + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Build + uses: andstor/jsdoc-action@v1 + with: + config_file: jsdoc.json + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} + publish_dir: ./docs \ No newline at end of file diff --git a/README.md b/README.md index ebd68f8..7f94e6d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## we_utils +# [we_utils](https://github.com/hexxone/we_utils) ### is a collection of utilities, mostly usefull when creating Wallpaper Engine Web Wallpapers with TypeScript / Webpack. @@ -6,9 +6,13 @@ I created this repository since I was previously copying back-and-forth lots of Keeping track of this stuff manually is annoying... +## [Documentation](https://hexxone.github.io/we_utils) + + ### Dependencies / Libraries - [typescript](https://www.typescriptlang.org/) for typization - [three.js](https://threejs.org/) & Examples for webgl rendering +- [wasc-worker](https://github.com/hexxone/wasc-worker) for ez AssemblyScript workers ### Features / Contents @@ -20,11 +24,11 @@ Keeping track of this stuff manually is annoying... - "document.ready" shorthand - ReloadHelper for displaying a loading bar - Smallog Logging & Filtering -- Stats.js definition file - WarnHelper for Seizure warnings - Wallpaper Engine iCUE Library (WEICUE) - Wallpaper Engine Wallpaper Adapter (WEWWA) - Worker-Loader definition file +- Stats.js definition file ### Used by diff --git a/docs/CComponent.html b/docs/CComponent.html index 3873b4a..9ff1c7b 100644 --- a/docs/CComponent.html +++ b/docs/CComponent.html @@ -1,849 +1,981 @@ - - - - - - - - CComponent - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    CComponent

    -
    - - - - - -
    - -
    - -

    CComponent()

    - -
    Base-Component for a TypeScript Web Wallpaper
    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new CComponent() - - -

    - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CComponent.ts, line 6 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - - - - - -

    Classes

    - -
    -
    CComponent
    -
    -
    - - - - - - - - - -
    -

    Members

    -
    - -
    - -

    - # - - - settings - - -

    - - - - -
    - main Settings, need to be overwritten with Specific settings -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    See:
    -
    - -
    - - - - - -

    - View Source - - CComponent.ts, line 12 - -

    - -
    - - - - - -
    - -
    - - - - -CSettings - - - - -

    - # - - - settings - - -

    - - - - -
    - main Settings, need to be overwritten with Specific settings -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    See:
    -
    - -
    - - - - - -

    - View Source - - CComponent.ts, line 66 - -

    - -
    - - - - - -
    - -
    -
    - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - applySetting(key, value) → {boolean} - - -

    - - - - -
    - will recursively try to set a setting with type and return success
    will also flag the module as needs-Update. -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    key - - -Object - - - -
    value - - -Object - - - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CComponent.ts, line 25 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    found
    - - -
    - - -boolean - - -
    - -
    - - -
    -
    - - - - -
    - -
    - - - -

    - # - - - - updateAll() - - -

    - - - - -
    - DO NOT OVERWWRITE !!!
    will recursively update all needed modules after settings changes -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CComponent.ts, line 39 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - updateSettings() → {Promise} - - -

    - - - - -
    - NEEDS TO BE OVERWRITTEN FOR DOING ACTIONS ON SETTINGS CHANGE.
    should usually get called automatically when needed.. no need for extra calling -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CComponent.ts, line 52 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    async commpletion event
    - - -
    - - -Promise - - -
    - -
    - - -
    -
    - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - + + + + + + + + CComponent + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    CComponent

    +
    + + + + + +
    + +
    + +

    CComponent()

    + +
    Base-Component for a TypeScript Web Wallpaper
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new CComponent() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 6 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    CComponent
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + +

    + # + + + children + + +

    + + + + +
    + Important: Append your child objects, for settings to be applied correctly! +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 16 + +

    + +
    + + + + + +
    + +
    + + + + +Array + + + + +

    + # + + + children + + +

    + + + + +
    + Important: Append your child objects, for settings to be applied correctly! +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 74 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + settings + + +

    + + + + +
    + main Settings, need to be overwritten with Specific settings +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 12 + +

    + +
    + + + + + +
    + +
    + + + + +CSettings + + + + +

    + # + + + settings + + +

    + + + + +
    + main Settings, need to be overwritten with Specific settings +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 68 + +

    + +
    + + + + + +
    + +
    +
    + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + applySetting(key, value) → {boolean} + + +

    + + + + +
    + will recursively try to set a setting with type and return success
    will also flag the module as needs-Update. +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +Object + + + +
    value + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 27 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    found
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + updateAll() + + +

    + + + + +
    + DO NOT OVERWWRITE !!!
    will recursively update all needed modules after settings changes +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 41 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + updateSettings() → {Promise} + + +

    + + + + +
    + NEEDS TO BE OVERWRITTEN FOR DOING ACTIONS ON SETTINGS CHANGE.
    should usually get called automatically when needed.. no need for extra calling +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 54 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    async commpletion event
    + + +
    + + +Promise + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/docs/CComponent.ts.html b/docs/CComponent.ts.html index 4078bd3..aea6e01 100644 --- a/docs/CComponent.ts.html +++ b/docs/CComponent.ts.html @@ -68,7 +68,7 @@ @@ -103,12 +103,14 @@

    CComponent.ts

    export class CComponent { private needsUpdate = false; - /** main Settings, need to be overwritten with Specific settings - * @see {CSettings} - */ + /** + * main Settings, need to be overwritten with Specific settings + */ public settings: CSettings = null; - /* Important: Append your child objects, for settings to be applied correctly! */ + /** + * Important: Append your child objects, for settings to be applied correctly! + */ public children: CComponent[] = []; /** diff --git a/docs/CSettings.html b/docs/CSettings.html index 867087d..8f9c0cb 100644 --- a/docs/CSettings.html +++ b/docs/CSettings.html @@ -1,463 +1,463 @@ - - - - - - - - CSettings - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    CSettings

    -
    - - - - - -
    - -
    - -

    CSettings()

    - -
    Base-Component Settings helper Core Settings interface, used for type-secure setting applying. All Settings-classes should be dereived from this one.
    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new CSettings() - - -

    - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    See:
    -
    - -
    - - - - - -

    - View Source - - CSettings.ts, line 20 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - - - - - - - - - - - - - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - apply(key, castedValue) → {boolean} - - -

    - - - - -
    - check if a certain key exists on a (dereived) object and the value type matches -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    key - - -string - - - -
    castedValue - - -Object - - - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CSettings.ts, line 27 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    success
    - - -
    - - -boolean - - -
    - -
    - - -
    -
    - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - + + + + + + + + CSettings + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    CSettings

    +
    + + + + + +
    + +
    + +

    CSettings()

    + +
    Base-Component Settings helper Core Settings interface, used for type-secure setting applying. All Settings-classes should be dereived from this one.
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new CSettings() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    See:
    +
    + +
    + + + + + +

    + View Source + + CSettings.ts, line 20 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + apply(key, castedValue) → {boolean} + + +

    + + + + +
    + check if a certain key exists on a (dereived) object and the value type matches +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +string + + + +
    castedValue + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CSettings.ts, line 27 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    success
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/docs/CSettings.ts.html b/docs/CSettings.ts.html index 802abd4..42b3b90 100644 --- a/docs/CSettings.ts.html +++ b/docs/CSettings.ts.html @@ -68,7 +68,7 @@ @@ -118,7 +118,7 @@

    CSettings.ts

    this[key] = castedValue; return true; } else { - Smallog.Error('CSettings Error: invalid type on: \'' + key + + Smallog.error('CSettings Error: invalid type on: \'' + key + '\'. Is: \'' + typeof this[key] + '\', applied: \'' + typeof castedValue + '\''); } } diff --git a/docs/CUESettings.html b/docs/CUESettings.html index 12a3afd..70ec6bc 100644 --- a/docs/CUESettings.html +++ b/docs/CUESettings.html @@ -66,7 +66,7 @@ diff --git a/docs/OfflinePlugin.html b/docs/OfflinePlugin.html index 78d5b61..d11118f 100644 --- a/docs/OfflinePlugin.html +++ b/docs/OfflinePlugin.html @@ -1,461 +1,461 @@ - - - - - - - - OfflinePlugin - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    OfflinePlugin

    -
    - - - - - -
    - -
    - -

    OfflinePlugin(options)

    - -
    This is a webpack plugin for easily making your webapp available Offline.
    It will simply output a json list of files to cache for the Service Worker on build.
    In your source, you just 'require' and 'Register()' the OfflineHelper.
    The OfflineHelper will then require the OfflineWorker in background.

    The OfflineWorker will then:
    1. launch itself
    2. load the 'offlinefiles.json' list
    3. cache all files in it
    4. return cached files if network fetching fails

    You can also ignore this plugin and create the 'offlinefiles.json' yourself.
    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new OfflinePlugin(options) - - -

    - - - - -
    - Intializes the plugin in the webpack build process -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    options - - -offliineSchema - - - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - offline/OfflinePlugin.js, line 87 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - - - - - - - - - - - - - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - apply(compiler) - - -

    - - - - -
    - Hook into the compilation process, find all target files and put them into a json file -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    compiler - - -Webpack.compiler - - - - object from webpack
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - offline/OfflinePlugin.js, line 104 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - + + + + + + + + OfflinePlugin + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    OfflinePlugin

    +
    + + + + + +
    + +
    + +

    OfflinePlugin(options)

    + +
    This is a webpack plugin for easily making your webapp available Offline.
    It will simply output a json list of files to cache for the Service Worker on build.
    In your source, you just 'require' and 'Register()' the OfflineHelper.
    The OfflineHelper will then require the OfflineWorker in background.

    The OfflineWorker will then:
    1. launch itself
    2. load the 'offlinefiles.json' list
    3. cache all files in it
    4. return cached files if network fetching fails

    You can also ignore this plugin and create the 'offlinefiles.json' yourself.
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new OfflinePlugin(options) + + +

    + + + + +
    + Intializes the plugin in the webpack build process +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    options + + +offliineSchema + + + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + offline/OfflinePlugin.js, line 87 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + apply(compiler) + + +

    + + + + +
    + Hook into the compilation process, find all target files and put them into a json file +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    compiler + + +Webpack.compiler + + + + object from webpack
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + offline/OfflinePlugin.js, line 104 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/docs/ReloadHelper.html b/docs/ReloadHelper.html index 5ee78b8..85330de 100644 --- a/docs/ReloadHelper.html +++ b/docs/ReloadHelper.html @@ -66,7 +66,7 @@ @@ -235,200 +235,6 @@

    Methods

    -

    - # - - - - injectCSS() - - -

    - - - - -
    - Make custom style -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - ReloadHelper.ts, line 44 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - - -
    - - - -

    - # - - - - injectHTML() - - -

    - - - - -
    - Make custom html elements -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - ReloadHelper.ts, line 91 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - @@ -128,6 +128,7 @@

    ReloadHelper.ts

    /** * Make custom style + * @ignore */ private injectCSS() { const st = document.createElement('style'); @@ -176,6 +177,7 @@

    ReloadHelper.ts

    /** * Make custom html elements + * @ignore */ private injectHTML() { const outer = document.createElement('div'); diff --git a/docs/ReloadSettings.html b/docs/ReloadSettings.html index d03a85a..0b801ee 100644 --- a/docs/ReloadSettings.html +++ b/docs/ReloadSettings.html @@ -66,7 +66,7 @@ diff --git a/docs/Stats.ts.html b/docs/Smallog.ts.html similarity index 57% rename from docs/Stats.ts.html rename to docs/Smallog.ts.html index 86a8abb..7d5784c 100644 --- a/docs/Stats.ts.html +++ b/docs/Smallog.ts.html @@ -7,7 +7,7 @@ - Stats.ts + Smallog.ts @@ -68,7 +68,7 @@ @@ -76,7 +76,7 @@

    Documentation

    Classes

    Source

    -

    Stats.ts

    +

    Smallog.ts

    @@ -85,7 +85,8 @@

    Stats.ts

    -
    /**
    +            
    
    +/**
     * @author hexxone / https://hexx.one
     *
     * @license
    @@ -94,36 +95,128 @@ 

    Stats.ts

    * See LICENSE file in the project root for full license information. */ +/* eslint-disable no-unused-vars */ + /** -* @ignore -* TypeScript Wrapper for mrdoob Stats.js -* Still requires Stats.js to be included! -*/ -declare interface Stats { - REVISION: number; - dom: HTMLDivElement; - addPanel(panel: Stats.Panel): Stats.Panel; - showPanel(id: number): void; - begin(): void; - end(): void; - update(): void; - domElement: HTMLDivElement; - setMode(id: number): void; + * trace exception calls + * @param {string} def error message + * @param {number} depth which call to pick + * @return {string} + * @ignore + */ +function traceCall(def: string, depth: number = 3) { + try { + throw new Error('TraceCall()'); + } catch (e) { + // Examine e.stack here + if (e.stack) { + const splt = e.stack.split(/\n/); + if (splt.length > depth) return '[' + splt[depth].trim().substring(3) + '] '; + } + } + return def; } -declare function Stats(): Stats; +/** + * @see {Smallog} + */ +export enum LogLevel { + /** + * Print only error messages + */ + Error = 0, + /** + * Print error and info messages + */ + Info = 1, + /** + * Print all messages + */ + Debug = 2 +} -declare namespace Stats { - interface Panel { - dom: HTMLCanvasElement; - update(value: number, maxValue: number): void; +/** + * Small logging util, with name/time prefix & log levels + */ +export module Smallog { + + let logLevel: LogLevel = LogLevel.Debug; // todo level Info for release + let preFix: string = '[Smallog] '; + let printTime: boolean = false; + + /** + * get logging output level + * @return {LogLevel} current + */ + export function getLevel() { + return logLevel; } - // eslint-disable-next-line no-unused-vars - function Panel(name?: string, fg?: string, bg?: string): Panel; -} + /** + * set logging output level + * @param {LogLevel} level new + */ + export function setLevel(level: LogLevel) { + logLevel = level; + } + + /** + * set logging prefix + * @param {string} pre + */ + export function setPrefix(pre: string) { + preFix = pre; + } -export default Stats; + /** + * set time prefix + * @param {boolean} print + */ + export function setPrintTime(print: boolean) { + printTime = print; + } + + /** + * print error message + * @param {string} msg log + * @param {string} hdr overwrite header + */ + export function error(msg: string, hdr: string = preFix) { + log(console.error, msg, traceCall(hdr)); + } + + /** + * print info message + * @param {string} msg log + * @param {string} hdr overwrite header + */ + export function info(msg: string, hdr: string = preFix) { + if (logLevel >= 2) hdr = traceCall(hdr); + if (logLevel >= 1) log(console.info, msg, hdr); + } + + /** + * print debug message + * @param {string} msg log + * @param {string} hdr overwrite header + */ + export function debug(msg: string, hdr: string = preFix) { + if (logLevel >= 2) log(console.debug, msg, traceCall(hdr)); + } + + /** + * internal logging function + * @param {Function} call log callback + * @param {string} msg text to print + * @param {string} hdr header to include + * @ignore + */ + function log(call: (...data: any) => void, msg: string, hdr: string) { + let m = msg; + if (printTime) m = ('[' + new Date().toLocaleString() + '] ') + m; + call(hdr + m); + } +}
    diff --git a/docs/Stats.html b/docs/Stats.html deleted file mode 100644 index 047a8a0..0000000 --- a/docs/Stats.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - - - Stats - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Interface

    -

    Stats

    -
    - - - - - -
    - -
    - -

    Stats

    - - -
    - -
    -
    - - - - - - -
    - - - - - - - - - - - - - - - - - - -
    Author:
    -
    -
      -
    • hexxone / https://hexx.one
    • -
    -
    - - - - - -
    License:
    -
    • Copyright (c) 2021 hexxone All rights reserved. Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
    - - - - - - - - - - -

    - View Source - - Stats.ts, line 4 - -

    - -
    - - - - -
    - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - - \ No newline at end of file diff --git a/docs/Util.ts.html b/docs/Util.ts.html index 2e2cf26..ea24760 100644 --- a/docs/Util.ts.html +++ b/docs/Util.ts.html @@ -68,7 +68,7 @@
    @@ -98,8 +98,8 @@

    Util.ts

    */ /** -* Helper function -* @return {Promise} resolve when site ready +* Helper function, resolves when html document is ready +* @return {Promise} */ export function waitReady() { return new Promise((resolve) => { diff --git a/docs/WEAS.html b/docs/WEAS.html index 55b32b6..4dd15af 100644 --- a/docs/WEAS.html +++ b/docs/WEAS.html @@ -1,1190 +1,985 @@ - - - - - - - - WEAS - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    WEAS

    -
    - - - - - -
    - -
    - -

    WEAS()

    - -
    WEAS
    Wallpaper Engine Audio Supplier makes working with audio easier.
    It will automatically start to receive and process the audio data which can then be accessed on the global object.
    DEPENDS ON:
    - Wallpaper Engine Web Wallpaper environment
    - audio-processing supported web wallpaper
    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new WEAS() - - -

    - - - - -
    - delay audio initialization until page ready -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEAS.ts, line 80 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - -

    Extends

    - - - - - - - - - - - - -

    Classes

    - -
    -
    WEAS
    -
    -
    - - - - - - - - - -
    -

    Members

    -
    - -
    - -

    - # - - - settings - - -

    - - - - -
    - main Settings, need to be overwritten with Specific settings -
    - - - - - -
    - - - - - - - - -
    Overrides:
    -
    - - - - - - - - - - - - - - - - - - - - -
    See:
    -
    - -
    - - - - - -

    - View Source - - CComponent.ts, line 12 - -

    - -
    - - - - - -
    - -
    -
    - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - applySetting(key, value) → {boolean} - - -

    - - - - -
    - will recursively try to set a setting with type and return success
    will also flag the module as needs-Update. -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    key - - -Object - - - -
    value - - -Object - - - -
    -
    - - - - - -
    - - - - - - - - -
    Overrides:
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CComponent.ts, line 25 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    found
    - - -
    - - -boolean - - -
    - -
    - - -
    -
    - - - - -
    - -
    - - - -

    - # - - - - getProps(dProps) → {Object} - - -

    - - - - -
    - converts calculated output property number-array to string-associative-array -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    dProps - - -ArrayLike.<number> - - - - processed properties
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEAS.ts, line 163 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - - -
    - - -Object - - -
    - -
    - - -
    -
    - - - - -
    - -
    - - - -

    - # - - - - hasAudio() → {boolean} - - -

    - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEAS.ts, line 218 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    false if:
    - processing is disabled
    - there is no data
    - the data is silent
    - data is too old (> 3s)
    - - -
    - - -boolean - - -
    - -
    - - -
    -
    - - - - -
    - -
    - - - -

    - # - - - async - - - - - realInit() - - -

    - - - - -
    - initializes audio processing pipeline and starts listening on audio data -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEAS.ts, line 100 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - updateAll() - - -

    - - - - -
    - DO NOT OVERWWRITE !!!
    will recursively update all needed modules after settings changes -
    - - - - - - - - - - - - - - -
    - - - - - - - - -
    Overrides:
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CComponent.ts, line 39 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - updateSettings() → {Promise} - - -

    - - - - -
    - !! CAVEAT: only available after init and module load !!
    Will send the processing settings to the WebAssembly module -
    - - - - - - - - - - - - - - -
    - - - - - - - - -
    Overrides:
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEAS.ts, line 178 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    finished event
    - - -
    - - -Promise - - -
    - -
    - - -
    -
    - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - + + + + + + + + WEAS + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    WEAS

    +
    + + + + + +
    + +
    + +

    WEAS()

    + +
    WEAS
    Wallpaper Engine Audio Supplier makes working with audio easier.
    It will automatically start to receive and process the audio data which can then be accessed on the global object.
    DEPENDS ON:
    - Wallpaper Engine Web Wallpaper environment
    - audio-processing supported web wallpaper
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new WEAS() + + +

    + + + + +
    + delay audio initialization until page ready +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 88 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + +

    Classes

    + +
    +
    WEAS
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + +

    + # + + + children + + +

    + + + + +
    + Important: Append your child objects, for settings to be applied correctly! +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 16 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + settings + + +

    + + + + +
    + main Settings, need to be overwritten with Specific settings +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 12 + +

    + +
    + + + + + +
    + +
    +
    + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + applySetting(key, value) → {boolean} + + +

    + + + + +
    + will recursively try to set a setting with type and return success
    will also flag the module as needs-Update. +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +Object + + + +
    value + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 27 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    found
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + hasAudio() → {boolean} + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 237 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    false if:
    - processing is disabled
    - there is no data
    - the data is silent
    - data is too old (> 3s)
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + updateAll() + + +

    + + + + +
    + DO NOT OVERWWRITE !!!
    will recursively update all needed modules after settings changes +
    + + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 41 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + updateSettings() → {Promise} + + +

    + + + + +
    + !! CAVEAT: only available after init and module load !!
    Will send the processing settings to the WebAssembly module +
    + + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 198 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    finished event
    + + +
    + + +Promise + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/docs/WEAS.ts.html b/docs/WEAS.ts.html index 472a4f8..d510623 100644 --- a/docs/WEAS.ts.html +++ b/docs/WEAS.ts.html @@ -68,7 +68,7 @@

    @@ -94,7 +94,6 @@

    WEAS.ts

    * See LICENSE file in the project root for full license information. */ -import {ASUtil} from '@assemblyscript/loader'; import {CComponent} from './CComponent'; import {CSettings} from './CSettings'; import {waitReady} from './Util'; @@ -201,6 +200,7 @@

    WEAS.ts

    /** * initializes audio processing pipeline * and starts listening on audio data + * @ignore */ private async realInit() { // if wallpaper engine context given, listen @@ -219,33 +219,42 @@

    WEAS.ts

    // register audio callback on module window['wallpaperRegisterAudioListener']((audioArray) => { - // check proof - if (!audioArray || !self.settings.audioprocessing) return; - if (audioArray.length != DAT_LEN) { - Smallog.Error('audioListener: received invalid audio data array. Length: ' + audioArray.length); + // Smallog.debug('Get Audio Data!'); + // check basic + if (!self.settings.audioprocessing || audioArray == null || audioArray.length != DAT_LEN) { + Smallog.error('audioListener: received invalid audio data array: ' + JSON.stringify([audioArray.length || null, audioArray])); return; } + // check nulls + let consecutiveNull = 0; + for (let i = 0; i < 15; i++) { + const aA = audioArray[Math.floor(Math.random() * audioArray.length)]; + if (aA == 0.0) consecutiveNull++; + else consecutiveNull = 0; + if (consecutiveNull > 10) { + Smallog.debug('Skipping received Null data!: ' + JSON.stringify(audioArray)); + return; + } + } // prepare data const start = performance.now(); self.inBuff.set(audioArray); // WRAP IN isolated Function ran inside worker - run(({module, instance, importObject, params}) => { - const {exports} = instance; + run(({module, instance, exports, params}) => { + const ex = instance.exports as any; const {data} = params[0]; - const io = importObject as ASUtil; // set audio data directly in module memory - io.__getFloat64ArrayView(exports.inputData).set(data); + exports.__getFloat64ArrayView(ex.inputData).set(data); // trigger processing processing - exports.update(); + ex.update(); // get copy of updated Data & Properties const r = { // => result - data: new Float64Array(io.__getFloat64ArrayView(exports.outputData)), - props: new Float64Array(io.__getFloat64ArrayView(exports.audioProps)), + data: new Float64Array(exports.__getFloat64ArrayView(ex.outputData)), + props: new Float64Array(exports.__getFloat64ArrayView(ex.audioProps)), }; - console.log(r); return r; }, // params passed to worker { @@ -257,12 +266,12 @@

    WEAS.ts

    const realProps = self.getProps(props); // apply actual last Data from worker self.lastAudio = { - time: start, + time: start / 1000, data, ...realProps, }; // print info - Smallog.info('Got Data from Worker! Time= ' + (performance.now() - start) + ', Data= ' + JSON.stringify(realProps)); + Smallog.debug('Got Data from Worker! Time= ' + (performance.now() - start) + ', Data= ' + JSON.stringify(realProps)); }); }); } @@ -271,6 +280,7 @@

    WEAS.ts

    * converts calculated output property number-array to string-associative-array * @param {ArrayLike<number>} dProps processed properties * @return {Object} + * @ignore */ private getProps(dProps: ArrayLike<number>) { const keys = Object.keys(PropIDs); @@ -301,11 +311,10 @@

    WEAS.ts

    } // isolated Function running inside worker - run(({module, instance, importObject, params}) => { - const {exports} = instance; + run(({module, instance, exports, params}) => { + const ex = instance.exports as any; const {data} = params[0]; - const io = importObject as ASUtil; - io.__getFloat64ArrayView(exports.audioSettings).set(data); + exports.__getFloat64ArrayView(ex.audioSettings).set(data); }, // Data passed to worker { diff --git a/docs/WEASettings.html b/docs/WEASettings.html index 76c34ff..5f36c68 100644 --- a/docs/WEASettings.html +++ b/docs/WEASettings.html @@ -66,7 +66,7 @@ @@ -167,7 +167,7 @@

    View Source - WEAS.ts, line 12 + WEAS.ts, line 20

    @@ -293,7 +293,7 @@

    View Source - WEAS.ts, line 16 + WEAS.ts, line 24

    @@ -370,7 +370,7 @@

    View Source - WEAS.ts, line 238 + WEAS.ts, line 256

    diff --git a/docs/WEICUE.html b/docs/WEICUE.html index 19d0f17..cf74c8c 100644 --- a/docs/WEICUE.html +++ b/docs/WEICUE.html @@ -1,2270 +1,1066 @@ - - - - - - - - WEICUE - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    WEICUE

    -
    - - - - - -
    - -
    - -

    WEICUE(weas)

    - -
    WEICUE
    Wallpaper Engine iCUE effects for web wallpapers
    Uses several different methods to create Lighting effects for Corsair ICUE devices.
    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new WEICUE(weas) - - -

    - - - - -
    - Starts listening for led/icue plugin and prepares helper elements -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    weas - - -WEAS - - - - Audio supplier for non-projection mode
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 48 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - -

    Extends

    - - - - - - - - - - - - -

    Classes

    - -
    -
    WEICUE
    -
    -
    - - - - - - - - - -
    -

    Members

    -
    - -
    - -

    - # - - - settings - - -

    - - - - -
    - main Settings, need to be overwritten with Specific settings -
    - - - - - -
    - - - - - - - - -
    Overrides:
    -
    - - - - - - - - - - - - - - - - - - - - -
    See:
    -
    - -
    - - - - - -

    - View Source - - CComponent.ts, line 12 - -

    - -
    - - - - - -
    - -
    -
    - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - applySetting(key, value) → {boolean} - - -

    - - - - -
    - will recursively try to set a setting with type and return success
    will also flag the module as needs-Update. -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    key - - -Object - - - -
    value - - -Object - - - -
    -
    - - - - - -
    - - - - - - - - -
    Overrides:
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CComponent.ts, line 25 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    found
    - - -
    - - -boolean - - -
    - -
    - - -
    -
    - - - - -
    - -
    - - - -

    - # - - - - gBlurCanvas(canvas, ctx, blur) - - -

    - - - - -
    - canvas blur helper function -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    canvas - - -HTMLCanvasElement - - - -
    ctx - - -CanvasRenderingContext2D - - - -
    blur - - -number - - - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 375 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - getArea(inPx) → {Object} - - -

    - - - - -
    - helper -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    inPx - - -boolean - - - - - - false - - suffix "px" string to number (allows direct css use)
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 339 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    area
    - - -
    - - -Object - - -
    - -
    - - -
    -
    - - - - -
    - -
    - - - -

    - # - - - - getEncodedCanvasImageData(imageData) → {string} - - -

    - - - - -
    - convert data for icue -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    imageData - - -ImageData - - - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 359 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - - -
    - - -string - - -
    - -
    - - -
    -
    - - - - -
    - -
    - - - -

    - # - - - - icueMessage(msg, waiting) - - -

    - - - - -
    - show a message by icue -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    msg - - -string - - - - - -
    waiting - - -boolean - - - - - - false - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 318 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - init() - - -

    - - - - -
    - Show waiting message and init canvas -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 399 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - initCUE(count) - - -

    - - - - -
    - will initialize ICUE api & usage -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    count - - -number - - - - Retries (will stop at 100)
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 450 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - injectCSS() - - -

    - - - - -
    - style for iCue messages, preview and helper -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 89 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - injectHTML() - - -

    - - - - -
    - Prepare html elements -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 133 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - updateAll() - - -

    - - - - -
    - DO NOT OVERWWRITE !!!
    will recursively update all needed modules after settings changes -
    - - - - - - - - - - - - - - -
    - - - - - - - - -
    Overrides:
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CComponent.ts, line 39 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - updateCanvas(mainCanvas) - - -

    - - - - -
    - copy main canvas portion to our helper -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    mainCanvas - - -HTMLCanvasElementq - - - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 521 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - updateFrame() - - -

    - - - - -
    - do the thing... -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 478 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - updateSettings() → {Promise} - - -

    - - - - -
    - show or hide preview -
    - - - - - - - - - - - - - - -
    - - - - - - - - -
    Overrides:
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 427 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    finished
    - - -
    - - -Promise - - -
    - -
    - - -
    -
    - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - + + + + + + + + WEICUE + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    WEICUE

    +
    + + + + + +
    + +
    + +

    WEICUE(weas)

    + +
    WEICUE
    Wallpaper Engine iCUE effects for web wallpapers
    Uses several different methods to create Lighting effects for Corsair ICUE devices.
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new WEICUE(weas) + + +

    + + + + +
    + Starts listening for led/icue plugin and prepares helper elements +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    weas + + +WEAS + + + + Audio supplier for non-projection mode
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEICUE.ts, line 48 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + +

    Classes

    + +
    +
    WEICUE
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + +

    + # + + + children + + +

    + + + + +
    + Important: Append your child objects, for settings to be applied correctly! +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 16 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + settings + + +

    + + + + +
    + main Settings, need to be overwritten with Specific settings +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 12 + +

    + +
    + + + + + +
    + +
    +
    + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + applySetting(key, value) → {boolean} + + +

    + + + + +
    + will recursively try to set a setting with type and return success
    will also flag the module as needs-Update. +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +Object + + + +
    value + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 27 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    found
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + updateAll() + + +

    + + + + +
    + DO NOT OVERWWRITE !!!
    will recursively update all needed modules after settings changes +
    + + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 41 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + updateCanvas(mainCanvas) + + +

    + + + + +
    + copy main canvas portion to our helper +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    mainCanvas + + +HTMLCanvasElementq + + + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEICUE.ts, line 530 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + updateSettings() → {Promise} + + +

    + + + + +
    + show or hide preview +
    + + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEICUE.ts, line 434 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    finished
    + + +
    + + +Promise + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/docs/WEICUE.ts.html b/docs/WEICUE.ts.html index 64fb329..4851086 100644 --- a/docs/WEICUE.ts.html +++ b/docs/WEICUE.ts.html @@ -68,7 +68,7 @@ @@ -147,7 +147,6 @@

    WEICUE.ts

    // runtime values public settings: CUESettings = new CUESettings(); - public isAvailable: boolean = false; public PAUSED: boolean = false; @@ -178,6 +177,7 @@

    WEICUE.ts

    /** * style for iCue messages, preview and helper + * @ignore */ private injectCSS() { const st = document.createElement('style'); @@ -223,6 +223,7 @@

    WEICUE.ts

    /** * Prepare html elements + * @ignore */ private injectHTML() { // create container @@ -409,6 +410,7 @@

    WEICUE.ts

    * show a message by icue * @param {string} msg * @param {boolean} waiting + * @ignore */ private icueMessage(msg: string, waiting :boolean = false) { Smallog.debug('MSG: ' + msg, ClassName); @@ -429,6 +431,7 @@

    WEICUE.ts

    * helper * @param {boolean} inPx suffix "px" string to number (allows direct css use) * @return {Object} area + * @ignore */ private getArea(inPx = false) { const sett = this.settings; @@ -450,6 +453,7 @@

    WEICUE.ts

    * convert data for icue * @param {ImageData} imageData * @return {string} + * @ignore */ private getEncodedCanvasImageData(imageData: ImageData) { const colorArray = []; @@ -467,6 +471,7 @@

    WEICUE.ts

    * @param {HTMLCanvasElement} canvas * @param {CanvasRenderingContext2D} ctx * @param {number} blur + * @ignore */ private gBlurCanvas(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, blur: number) { let sum = 0; @@ -492,6 +497,7 @@

    WEICUE.ts

    /** * Show waiting message and init canvas + * @ignore */ private init() { const sett = this.settings; @@ -544,6 +550,7 @@

    WEICUE.ts

    /** * will initialize ICUE api & usage * @param {number} count Retries (will stop at 100) + * @ignore */ private initCUE(count) { // wait for plugins @@ -572,6 +579,7 @@

    WEICUE.ts

    /** * do the thing... + * @ignore */ private updateFrame() { const sett = this.settings; @@ -612,9 +620,9 @@

    WEICUE.ts

    } /** - * copy main canvas portion to our helper - * @param {HTMLCanvasElementq} mainCanvas - */ + * copy main canvas portion to our helper + * @param {HTMLCanvasElementq} mainCanvas + */ public updateCanvas(mainCanvas: HTMLCanvasElement) { const sett = this.settings; if (!this.isAvailable || !mainCanvas || sett.icue_mode == 0 || this.icueDevices.length < 1) return; diff --git a/docs/WEWA.ts.html b/docs/WEWA.ts.html index 077724a..c553d47 100644 --- a/docs/WEWA.ts.html +++ b/docs/WEWA.ts.html @@ -68,7 +68,7 @@ @@ -157,7 +157,7 @@

    WEWA.ts

    * - cf longer cache policy (2d?) * - <img alt's * - <form <input <label's - */ +*/ export class WEWWA { private project: any = null; @@ -176,10 +176,10 @@

    WEWA.ts

    private isPaused: boolean = false; /** - * Check if we are running in Web-Mode - * if yes => iniitialize, else => do nothing - * @param {Function} finished Callback - */ + * Check if we are running in Web-Mode + * if yes => iniitialize, else => do nothing + * @param {Function} finished Callback for initializing the wallpaper + */ constructor(finished) { if (window['wallpaperRegisterAudioListener']) { Smallog.info('detected wallpaper engine => Standby.', LogHead); @@ -224,12 +224,13 @@

    WEWA.ts

    } /** - * Initialize the Web Adapter - */ + * Initialize the Web Adapter + * @ignore + */ private init() { myFetch('project.json', 'json').then((proj) => { if (proj.type != 'web') { - Smallog.Error('Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...', LogHead); + Smallog.error('Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...', LogHead); return; } this.project = proj; @@ -242,8 +243,9 @@

    WEWA.ts

    } /** - * Load last settings from localStorage - */ + * Load last settings from localStorage + * @ignore + */ private loadStorage() { const props = this.project.general.properties; const last = localStorage.getItem('wewwaLastProps'); @@ -259,8 +261,9 @@

    WEWA.ts

    } /** - * CSS Insertion - */ + * CSS Insertion + * @ignore + */ private addStyle() { const st = document.createElement('style'); // precalculation @@ -390,6 +393,7 @@

    WEWA.ts

    /** * HTML Creation * @param {string} lang WE language + * @ignore */ private addMenu(lang) { const self = this; @@ -427,6 +431,7 @@

    WEWA.ts

    * Adds the Menu Icon * @param {Function} ce CreateElement * @param {Element} menu + * @ignore */ private addMenuIcon(ce: (e: any) => any, menu = this.htmlMenu) { const icon = this.htmlIcon = ce('div'); @@ -451,14 +456,15 @@

    WEWA.ts

    } /** - * Adds the actual Wallpaper Props as HTML - * @param {Function} ce Create Element wrapper - * @param {Object} proj project - * @param {object} self this - * @param {string} lang - * @param {object} props - * @param {Element} menu - */ + * Adds the actual Wallpaper Props as HTML + * @param {Function} ce Create Element wrapper + * @param {Object} proj project + * @param {object} self this + * @param {string} lang + * @param {object} props + * @param {Element} menu + * @ignore + */ private addMenuSettings(ce: (e: any) => any, proj: any, self: this, lang: string, props: any, menu = this.htmlMenu) { const tbl = ce('table'); tbl.innerHTML = '<col style="width:50%"> <col style="width:30%"> <col style="width:20%">'; @@ -533,9 +539,10 @@

    WEWA.ts

    } /** - * Add missing default localization strings - * @param {Object} local - */ + * Add missing default localization strings + * @param {Object} local + * @ignore + */ private mergeLocals(local: any) { const locDefs = { 'ui_browse_properties_scheme_color': 'Scheme color', @@ -551,10 +558,11 @@

    WEWA.ts

    } /** - * Adds the Footer Link to the Menu - * @param {Function} ce create element - * @param {Element} menu - */ + * Adds the Footer Link to the Menu + * @param {Function} ce create element + * @param {Element} menu + * @ignore + */ private addMenuFooter(ce: (e: any) => any, menu = this.htmlMenu) { const preFoot = ce('div'); preFoot.innerHTML = '<hr>'; @@ -591,8 +599,9 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** - * Add Language Menu - */ + * Add Language Menu + * @ignore + */ private makeMenuLocalization(ce: (e: any) => any, lang, local, props) { const self = this; // add html struct @@ -645,8 +654,9 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** - * Add Audio Menu - */ + * Add Audio Menu + * @ignore + */ private addMenuAudio(ce: (e: any) => any, tblBody: any) { // audio input methods const row = ce('tr'); @@ -734,8 +744,9 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** - * Add preview Image, Title and Link - */ + * Add preview Image, Title and Link + * @ignore + */ private addMenuHeader(ce: (e: any) => any, proj: any, menu = this.htmlMenu) { const preview = ce('img'); preview.setAttribute('src', proj.preview); @@ -753,8 +764,9 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** - * Create an HTML Menu Item from project json property - */ + * Create an HTML Menu Item from project json property + * @ignore + */ private createItem(prop, itm) { if (!itm.type || itm.type == 'hidden') return null; const self = this; @@ -852,7 +864,7 @@

    WEWA.ts

    break; default: - Smallog.Error('unkown setting type: ' + itm.type, LogHead); + Smallog.error('unkown setting type: ' + itm.type, LogHead); break; } @@ -892,16 +904,16 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** - * Callback for UI-Settings changes - * Will apply them to the storage and running wallaper. - */ + * Callback for UI-Settings changes + * Will apply them to the storage and running wallaper. + */ public setProperty(prop, elm) { // get the type and apply the value const props = this.project.general.properties; // check for legit setting... if (!props[prop]) { - Smallog.Error('SetProperty name not found: ' + prop, LogHead); + Smallog.error('SetProperty name not found: ' + prop, LogHead); return; } @@ -931,11 +943,11 @@

    WEWA.ts

    if (elm.name.includes('_out_')) { const inpt: any = document.querySelector('#wewwa_' + prop); if (inpt) inpt.value = elm.value; - else Smallog.Error('Slider not found: ' + prop, LogHead); + else Smallog.error('Slider not found: ' + prop, LogHead); } else { const slide: any = document.querySelector('#wewwa_out_' + prop); if (slide) slide.value = elm.value; - else Smallog.Error('Numericupdown not found: ' + prop, LogHead); + else Smallog.error('Numericupdown not found: ' + prop, LogHead); } case 'combo': case 'textinput': @@ -946,10 +958,11 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** - * will load the given file and return it as dataURL. - * this way we can easily store whole files in the configuration & localStorage. - * its not safe that this works with something else than image files. - */ + * will load the given file and return it as dataURL. + * this way we can easily store whole files in the configuration & localStorage. + * its not safe that this works with something else than image files. + * @ignore + */ private loadXHRSaveLocal(url, resCall) { myFetch(url, 'blob').then((resp) => { // Read out file contents as a Data URL @@ -962,8 +975,8 @@

    WEWA.ts

    } /** - * Show or hide menu items based on eval condition - */ + * Show or hide menu items based on eval condition + */ public evaluateSettings() { const wewwaProps = this.project.general.properties; localStorage.setItem('wewwaLastProps', JSON.stringify(wewwaProps)); @@ -996,7 +1009,7 @@

    WEWA.ts

    try { visible = eval(cprop) == true; } catch (e) { - Smallog.Error('Error: (' + cprop + ') for: ' + p + ' => ' + e, LogHead); + Smallog.error('Error: (' + cprop + ') for: ' + p + ' => ' + e, LogHead); } } @@ -1032,8 +1045,8 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** - * Send one or more properties to the Wallpaper - */ + * Send one or more properties to the Wallpaper + */ public applyProp(prop) { const wpl = window['wallpaperPropertyListener']; if (wpl && wpl.applyUserProperties) { @@ -1043,8 +1056,8 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** - * Send paused-status to the Wallpaper - */ + * Send paused-status to the Wallpaper + */ public setPaused(val: boolean) { const wpl = window['wallpaperPropertyListener']; if (this.isPaused == val) return; @@ -1082,8 +1095,9 @@

    WEWA.ts

    // ------------------------------------- /** - * Request microphone from browser - */ + * Request microphone from browser + * @ignore + */ private requestMicrophone() { navigator.mediaDevices.getUserMedia({ audio: true, @@ -1100,7 +1114,7 @@

    WEWA.ts

    this.source.connect(this.analyser); this.startAudioInterval(); }).catch((err) => { - Smallog.Error(err, LogHead); + Smallog.error(err, LogHead); if (location.protocol != 'https:') { const r = confirm('Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS for this feature to work! Press \'ok\'/\'yes\' to get redirected to HTTPS and try again.'); if (r) window.location.href = window.location.href.replace('http', 'https'); @@ -1110,11 +1124,12 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** - * html5 audio analyser gives us mono data from 0(bass) to 128(treble) - * however, wallpaper engine expects stereo data in following format: - * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) - * so we do some array transformation... and divide by 255 (8bit-uint becomes float) - */ + * html5 audio analyser gives us mono data from 0(bass) to 128(treble) + * however, wallpaper engine expects stereo data in following format: + * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) + * so we do some array transformation... and divide by 255 (8bit-uint becomes float) + * @ignore + */ private convertAudio(data) { const stereo = []; let sIdx = 0; @@ -1127,8 +1142,9 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** - * Start the audio processing & analyzer - */ + * Start the audio processing & analyzer + * @ignore + */ private initiateAudio(data) { // clear up this.stopAudioInterval(); @@ -1155,15 +1171,16 @@

    WEWA.ts

    } /** - * Start the processing loop - */ + * Start the processing loop + * @ignore + */ private startAudioInterval() { const data = new Uint8Array(128); // 33ms ~~ 30fps this.audioInterval = window.setInterval(() => { if (this.audioCallback == null) { this.stopAudioInterval(); - Smallog.Error('no AudioCallback!', LogHead); + Smallog.error('no AudioCallback!', LogHead); return; } this.analyser.getByteFrequencyData(data); @@ -1175,8 +1192,8 @@

    WEWA.ts

    } /** - * Stop the processing loop - */ + * Stop the processing loop + */ public stopAudioInterval() { window['persistAudioStream'] = null; document.getElementById('wewwaAudioInput').setAttribute('value', ''); diff --git a/docs/WEWWA.html b/docs/WEWWA.html index 0e07852..80f1168 100644 --- a/docs/WEWWA.html +++ b/docs/WEWWA.html @@ -1,2891 +1,812 @@ - - - - - - - - WEWWA - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    WEWWA

    -
    - - - - - -
    - -
    - -

    WEWWA(finished)

    - -
    WEWWA
    Wallpaper Engine Web Wallpaper Adapter
    This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine Web-Wallpaper project - so you can test, run & configure it from a normal web browser.
    REQUIREMENTS:
    - HTML5 Browser
    - the "project.json" needs to be in the root folder like "index.html"
    - this file needs to be included/built in your "index.html"

    FEATURES:
    - automatically detecting if the web wallpaper is opened by wallpaper engine or browser
    - if opened by wallpaper engine, nothing will happen
    - if opened by a browser:
    - use a ServiceWorker to make page always available offline
    - automatically load the "project.json"
    - parse the settings, languages & conditions
    - add respective html elements for each setting type & condition
    - put these elements into an option menu which can be hidden
    - check localStorage for already saved/customized values
    - apply all settings once
    - react to changes made in the ui and update them in the wallpaper
    - save changes made in the ui to localStorage
    - Annoying Cookie Popup (Thanks DSGVO)
    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new WEWWA(finished) - - -

    - - - - -
    - Check if we are running in Web-Mode if yes => iniitialize, else => do nothing -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    finished - - -function - - - - Callback
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    To Do:
    -
    -
      -
    • - inject "audio processing" setting lighthouse: - image explicit width/height - cf longer cache policy (2d?) - -
    -
    - - - -

    - View Source - - WEWA.ts, line 72 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - - - - - -

    Classes

    - -
    -
    WEWWA
    -
    -
    - - - - - - - - - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - addMenu(lang) - - -

    - - - - -
    - HTML Creation -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    lang - - -string - - - - WE language
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 290 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - addMenuAudio() - - -

    - - - - -
    - Add Audio Menu -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 536 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - addMenuFooter(ce, menu) - - -

    - - - - -
    - Adds the Footer Link to the Menu -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    ce - - -function - - - - create element
    menu - - -Element - - - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 443 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - addMenuHeader() - - -

    - - - - -
    - Add preview Image, Title and Link -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 613 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - addMenuIcon(ce, menu) - - -

    - - - - -
    - Adds the Menu Icon -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    ce - - -function - - - - CreateElement
    menu - - -Element - - - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 321 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - addMenuSettings(ce, proj, self, lang, props, menu) - - -

    - - - - -
    - Adds the actual Wallpaper Props as HTML -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    ce - - -function - - - - Create Element wrapper
    proj - - -Object - - - - project
    self - - -object - - - - this
    lang - - -string - - - -
    props - - -object - - - -
    menu - - -Element - - - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 353 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - addStyle() - - -

    - - - - -
    - CSS Insertion -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 161 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - applyProp() - - -

    - - - - -
    - Send one or more properties to the Wallpaper -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 897 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - convertAudio() - - -

    - - - - -
    - html5 audio analyser gives us mono data from 0(bass) to 128(treble) however, wallpaper engine expects stereo data in following format: 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) so we do some array transformation... and divide by 255 (8bit-uint becomes float) -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 972 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - createItem() - - -

    - - - - -
    - Create an HTML Menu Item from project json property -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 631 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - evaluateSettings() - - -

    - - - - -
    - Show or hide menu items based on eval condition -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 829 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - init() - - -

    - - - - -
    - Initialize the Web Adapter -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 128 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - initiateAudio() - - -

    - - - - -
    - Start the audio processing & analyzer -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 985 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - loadStorage() - - -

    - - - - -
    - Load last settings from localStorage -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 145 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - loadXHRSaveLocal() - - -

    - - - - -
    - will load the given file and return it as dataURL. this way we can easily store whole files in the configuration & localStorage. its not safe that this works with something else than image files. -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 816 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - makeMenuLocalization() - - -

    - - - - -
    - Add Language Menu -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 477 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - mergeLocals(local) - - -

    - - - - -
    - Add missing default localization strings -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    local - - -Object - - - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 424 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - requestMicrophone() - - -

    - - - - -
    - Request microphone from browser -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 942 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - setPaused() - - -

    - - - - -
    - Send paused-status to the Wallpaper -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 907 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - setProperty() - - -

    - - - - -
    - Callback for UI-Settings changes Will apply them to the storage and running wallaper. -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 760 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - startAudioInterval() - - -

    - - - - -
    - Start the processing loop -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 1009 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - stopAudioInterval() - - -

    - - - - -
    - Stop the processing loop -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEWA.ts, line 1028 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - + + + + + + + + WEWWA + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    WEWWA

    +
    + + + + + +
    + +
    + +

    WEWWA(finished)

    + +
    WEWWA
    Wallpaper Engine Web Wallpaper Adapter
    This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine Web-Wallpaper project - so you can test, run & configure it from a normal web browser.
    REQUIREMENTS:
    - HTML5 Browser
    - the "project.json" needs to be in the root folder like "index.html"
    - this file needs to be included/built in your "index.html"

    FEATURES:
    - automatically detecting if the web wallpaper is opened by wallpaper engine or browser
    - if opened by wallpaper engine, nothing will happen
    - if opened by a browser:
    - use a ServiceWorker to make page always available offline
    - automatically load the "project.json"
    - parse the settings, languages & conditions
    - add respective html elements for each setting type & condition
    - put these elements into an option menu which can be hidden
    - check localStorage for already saved/customized values
    - apply all settings once
    - react to changes made in the ui and update them in the wallpaper
    - save changes made in the ui to localStorage
    - Annoying Cookie Popup (Thanks DSGVO)
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new WEWWA(finished) + + +

    + + + + +
    + Check if we are running in Web-Mode if yes => iniitialize, else => do nothing +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    finished + + +function + + + + Callback for initializing the wallpaper
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    To Do:
    +
    +
      +
    • - inject "audio processing" setting lighthouse: - image explicit width/height - cf longer cache policy (2d?) - +
    +
    + + + +

    + View Source + + WEWA.ts, line 72 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    WEWWA
    +
    +
    + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + applyProp() + + +

    + + + + +
    + Send one or more properties to the Wallpaper +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEWA.ts, line 910 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + evaluateSettings() + + +

    + + + + +
    + Show or hide menu items based on eval condition +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEWA.ts, line 842 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + setPaused() + + +

    + + + + +
    + Send paused-status to the Wallpaper +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEWA.ts, line 920 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + setProperty() + + +

    + + + + +
    + Callback for UI-Settings changes Will apply them to the storage and running wallaper. +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEWA.ts, line 772 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + stopAudioInterval() + + +

    + + + + +
    + Stop the processing loop +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEWA.ts, line 1045 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/docs/WarnHelper.html b/docs/WarnHelper.html index a98c3bf..5ddb77c 100644 --- a/docs/WarnHelper.html +++ b/docs/WarnHelper.html @@ -1,926 +1,1111 @@ - - - - - - - - WarnHelper - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    WarnHelper

    -
    - - - - - -
    - -
    - -

    WarnHelper()

    - -
    Seizure warning display
    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new WarnHelper() - - -

    - - - - -
    - Create and prepare once document ready -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WarnHelper.ts, line 36 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - - - - - -

    Classes

    - -
    -
    WarnHelper
    -
    -
    - - - - - - - - - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - hide() → {Promise} - - -

    - - - - -
    - Hide warning -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WarnHelper.ts, line 107 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    hidden
    - - -
    - - -Promise - - -
    - -
    - - -
    -
    - - - - -
    - -
    - - - -

    - # - - - - injectCSS() - - -

    - - - - -
    - Make custom style -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WarnHelper.ts, line 51 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - injectHTML() - - -

    - - - - -
    - Make custom html -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WarnHelper.ts, line 71 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - setText() - - -

    - - - - -
    - Set html text value -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WarnHelper.ts, line 79 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - -

    - # - - - - show() → {Promise} - - -

    - - - - -
    - Show the warning -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WarnHelper.ts, line 86 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    hidden again
    - - -
    - - -Promise - - -
    - -
    - - -
    -
    - - - - -
    - -
    - - - -

    - # - - - - updateSettings() → {Promise} - - -

    - - - - -
    - Settings have been changed -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WarnHelper.ts, line 123 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    finished
    - - -
    - - -Promise - - -
    - -
    - - -
    -
    - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - + + + + + + + + WarnHelper + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    WarnHelper

    +
    + + + + + +
    + +
    + +

    WarnHelper()

    + +
    Seizure warning display
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new WarnHelper() + + +

    + + + + +
    + Create and prepare once document ready +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WarnHelper.ts, line 38 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + +

    Classes

    + +
    +
    WarnHelper
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + +

    + # + + + children + + +

    + + + + +
    + Important: Append your child objects, for settings to be applied correctly! +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 16 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + settings + + +

    + + + + +
    + main Settings, need to be overwritten with Specific settings +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 12 + +

    + +
    + + + + + +
    + +
    +
    + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + applySetting(key, value) → {boolean} + + +

    + + + + +
    + will recursively try to set a setting with type and return success
    will also flag the module as needs-Update. +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +Object + + + +
    value + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 27 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    found
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + hide() → {Promise} + + +

    + + + + +
    + Hide warning +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WarnHelper.ts, line 112 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    hidden
    + + +
    + + +Promise + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + show() → {Promise} + + +

    + + + + +
    + Show the warning +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WarnHelper.ts, line 91 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    hidden again
    + + +
    + + +Promise + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + updateAll() + + +

    + + + + +
    + DO NOT OVERWWRITE !!!
    will recursively update all needed modules after settings changes +
    + + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 41 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + updateSettings() → {Promise} + + +

    + + + + +
    + Settings have been changed +
    + + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WarnHelper.ts, line 128 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    finished
    + + +
    + + +Promise + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/docs/WarnHelper.ts.html b/docs/WarnHelper.ts.html index 2c188c9..1eb14b8 100644 --- a/docs/WarnHelper.ts.html +++ b/docs/WarnHelper.ts.html @@ -68,7 +68,7 @@ @@ -109,6 +109,7 @@

    WarnHelper.ts

    /** * @TODO test getting text +* @extends {CSettings} */ class WarnSettings extends CSettings { seizure_warning: boolean = true; @@ -119,6 +120,7 @@

    WarnHelper.ts

    /** * Seizure warning display +* @extends {CComponent} */ export class WarnHelper extends CComponent { public settings: WarnSettings = new WarnSettings(); @@ -142,6 +144,7 @@

    WarnHelper.ts

    /** * Make custom style + * @ignore */ private injectCSS() { const st = document.createElement('style'); @@ -163,6 +166,7 @@

    WarnHelper.ts

    /** * Make custom html + * @ignore */ private injectHTML() { this.element = document.createElement('div'); @@ -172,6 +176,7 @@

    WarnHelper.ts

    /** * Set html text value + * @ignore */ private setText() { this.element.innerHTML = this.settings.seizure_text.replace('\r\n', '<br />'); diff --git a/docs/WarnSettings.html b/docs/WarnSettings.html index 5f9905e..dc4be1d 100644 --- a/docs/WarnSettings.html +++ b/docs/WarnSettings.html @@ -66,7 +66,7 @@ @@ -172,7 +172,7 @@

    View Source - WarnHelper.ts, line 24 + WarnHelper.ts, line 25

    @@ -207,6 +207,146 @@

    +

    Extends

    + + + + + + + + + + + + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + apply(key, castedValue) → {boolean} + + +

    + + + + +
    + check if a certain key exists on a (dereived) object and the value type matches +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +string + + + +
    castedValue + + +Object + + + +
    +
    + + + + + +
    @@ -215,11 +355,92 @@

    +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CSettings.ts, line 27 + +

    + +

    + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    success
    + +
    + + +boolean + +
    +
    + + +
    +
    + + + +
    + +
    +
    diff --git a/docs/WascBuilderPlugin.html b/docs/WascBuilderPlugin.html deleted file mode 100644 index 92a6273..0000000 --- a/docs/WascBuilderPlugin.html +++ /dev/null @@ -1,435 +0,0 @@ - - - - - - - - WascBuilderPlugin - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    WascBuilderPlugin

    -
    - - - - - -
    - -
    - -

    WascBuilderPlugin(options)

    - -
    This is a webpack plugin
    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new WascBuilderPlugin(options) - - -

    - - - - -
    - Intializes the plugin in the webpack build process -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    options - - -wascSchema - - - -
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - wasc-worker/WascBuilderPlugin.js, line 48 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - - - - - - - - - - - - - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - cleanUp() → {Promise} - - -

    - - - - -
    - delete all files in the output dir -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - wasc-worker/WascBuilderPlugin.js, line 187 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    async finished event
    - - -
    - - -Promise - - -
    - -
    - - -
    -
    - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - - \ No newline at end of file diff --git a/docs/WascInterface.html b/docs/WascInterface.html deleted file mode 100644 index cc5bd24..0000000 --- a/docs/WascInterface.html +++ /dev/null @@ -1,251 +0,0 @@ - - - - - - - - WascInterface - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    WascInterface

    -
    - - - - - -
    - -
    - -

    WascInterface()

    - -
    The shared interface for loading a module
    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new WascInterface() - - -

    - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - wasc-worker/WascInterface.ts, line 5 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - - \ No newline at end of file diff --git a/docs/fonts/OpenSans-Bold-webfont.eot b/docs/fonts/OpenSans-Bold-webfont.eot deleted file mode 100644 index 5d20d916338a5890a033952e2e07ba7380f5a7d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19544 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 diff --git a/docs/fonts/OpenSans-Bold-webfont.svg b/docs/fonts/OpenSans-Bold-webfont.svg deleted file mode 100644 index 3ed7be4..0000000 --- a/docs/fonts/OpenSans-Bold-webfont.svg +++ /dev/null @@ -1,1830 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/fonts/OpenSans-Bold-webfont.woff b/docs/fonts/OpenSans-Bold-webfont.woff deleted file mode 100644 index 1205787b0ed50db71ebd4f8a7f85d106721ff258..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22432 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 ig3W - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/fonts/OpenSans-BoldItalic-webfont.woff b/docs/fonts/OpenSans-BoldItalic-webfont.woff deleted file mode 100644 index ed760c0628b6a0026041f5b8bba466a0471fd2e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23048 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/fonts/OpenSans-Italic-webfont.woff b/docs/fonts/OpenSans-Italic-webfont.woff deleted file mode 100644 index ff652e64356b538c001423b6aedefcf1ee66cd17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23188 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 diff --git a/docs/fonts/OpenSans-Light-webfont.svg b/docs/fonts/OpenSans-Light-webfont.svg deleted file mode 100644 index 11a472c..0000000 --- a/docs/fonts/OpenSans-Light-webfont.svg +++ /dev/null @@ -1,1831 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/fonts/OpenSans-Light-webfont.woff b/docs/fonts/OpenSans-Light-webfont.woff deleted file mode 100644 index e786074813a27d0a7a249047832988d5bf0fe756..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22248 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 diff --git a/docs/fonts/OpenSans-LightItalic-webfont.eot b/docs/fonts/OpenSans-LightItalic-webfont.eot deleted file mode 100644 index 8f445929ffb03b50e98c2a2f7d831a0cb1b276a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20535 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 Z%0#`NexefPFV?Xj51H#Y#AC7WXn+Jg($4?@ diff --git a/docs/fonts/OpenSans-LightItalic-webfont.svg b/docs/fonts/OpenSans-LightItalic-webfont.svg deleted file mode 100644 index 431d7e3..0000000 --- a/docs/fonts/OpenSans-LightItalic-webfont.svg +++ /dev/null @@ -1,1835 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/fonts/OpenSans-LightItalic-webfont.woff b/docs/fonts/OpenSans-LightItalic-webfont.woff deleted file mode 100644 index 43e8b9e6cc061ff17fd2903075cbde12715512b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23400 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 @@ -156,15 +156,11 @@

    Members

    -

    - # - - - constant - +

    + # - offliineSchema + LogLevel

    @@ -172,10 +168,6 @@

    -
    - schema for options object -
    - @@ -211,7 +203,7 @@

    See:
    @@ -220,9 +212,9 @@

    - View Source + View Source - offline/OfflinePlugin.js, line 23 + Smallog.ts, line 35

    @@ -236,15 +228,11 @@

    -

    - # - - - constant - +

    + # - wascSchema + LogLevel[undefined]

    @@ -253,7 +241,7 @@

    - schema for options object + Print only error messages
    @@ -287,22 +275,15 @@

    - -
    See:
    -
    - -

    - View Source + View Source - wasc-worker/WascBuilderPlugin.js, line 27 + Smallog.ts, line 40

    @@ -314,144 +295,91 @@

    -

    - - - - -
    -

    Methods

    -
    -
    - +

    + # -

    - # - - - - getAllFiles(baseDir, subDir, arrayOfFiles) → {array} - - -

    + + LogLevel[undefined] + +

    )CdQ3F!eKyF|9D2V!F-rhUpJ8J+l7g0OBBVM#THTI&O8)>EGR%u6Gd9D9pm5!S}%&6DxW}^f|7=Y zPoO3(pTZY#?(7(|!5}5Nn!D%DotZmlW)?smSMcEE<^aT$6gw#LlwubPI9BYTffL0! zyu-EPCnz{Y#ZR&1d{F!hr_NW!&#~mXis$jseXDo@U)-kR7sMBeUt-T&RQw9By@BF9 z3f?cpmw4m-R{RHncaC**(V--ipJ<~6LkW2fi6RVfh%vcYt9@z>&M0LBSf-Q|Et8wU zCt43_*JB)mHR71wb`K@~5Cizwp{`A2uuJ^_Bcl3k{7ree$8&@l?;^2nagS+NqCDBfkB?pJws=PbK~+A7|2 z{gCDJKI-i%m4LD$n{WIwWR|c+NRy`C1#)1sSBI7FiH6z-QkhY&Q_|%I3exQ zQ`X1M?cZH4^M&BSyr;2z$+^SZUMA*0001Z+HKHROw(}?!13=vX`$@Br+fGR zZ%e`5O6%Txi$Yrz0gF{}p>fY>OnlS0Uevf}oDXW;D{d2gcE<2)oFcV80@g$H)63L{HN*d{8kVzKVW(;E)$9N_%kx5Ku3R9WJbY?JW^G#k0Wdx>E$NBBVtKRLiL?sA*s%w`TdsNz1=+~FRNdB8&+@iBD0 zXFTC4C-8-Cwv(4U=LLQ~^Oa4^rG|OTr5?ItoaPMYxxh`%a*kVU z;HYGAjq6;IY{`*awo0DlOMw(hkrYdb(O28l;MYvSx*ChcQW4f^QL5UdE3HbqvbxB$pfSg`>Cj#;?~00;nMAg}==M6d%RaIhCe zARtS)01i=0um)3FSgr#ump{<1pq_<0a34Kp8x=7I1^|9 diff --git a/docs/fonts/OpenSans-Regular-webfont.eot b/docs/fonts/OpenSans-Regular-webfont.eot deleted file mode 100644 index 6bbc3cf58cb011a6b4bf3cb1612ce212608f7274..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19836 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/fonts/OpenSans-Regular-webfont.woff b/docs/fonts/OpenSans-Regular-webfont.woff deleted file mode 100644 index e231183dce4c7b452afc9e7799586fd285e146f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22660 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 ML;wL%uV=LY00kR;TmS$7 diff --git a/docs/global.html b/docs/global.html index 8c2768c..d25a3e6 100644 --- a/docs/global.html +++ b/docs/global.html @@ -66,7 +66,7 @@

    - - -
    - list files recursively -
    - +
    + Print error and info messages +
    +
    -
    Parameters:
    -
    - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - - + + - + - - - - - + - + - + +

    + View Source + + Smallog.ts, line 44 + +

    + + - - - - + - - - - - - +
    - +

    + # + + + LogLevel[undefined] + + +

    - -
    - - - -
    NameTypeDescription
    baseDir - - -strring + + - - start directory
    subDir - - -string + + + - - sub directory
    arrayOfFiles - - -array - - - - result files
    +
    + Print all messages
    @@ -491,9 +419,9 @@
    Parameters:

    - View Source + View Source - offline/OfflinePlugin.js, line 48 + Smallog.ts, line 48

    @@ -503,128 +431,104 @@
    Parameters:
    +
    + +
    +

    + # + + + constant + + + + offlineSchema + + +

    +
    + schema for options object +
    +
    - - -
    -
    -
    - - - -
    -
    arrayOfFiles
    + + -
    - - -array + -
    -
    - -
    -
    + + + + -
    - -
    + + -

    - # - - - - getTransferableParams(params) → {Object} - - -

    +
    See:
    +
    + +
    -
    - Filter out parameters for passing them into WebWorkers -
    - - - - - - - - - -
    Parameters:
    -
    - - - - - - - + +

    + View Source + + offline/OfflinePlugin.js, line 23 + +

    + + - - - - - - - + - - - - - - +
    - +

    + # + + + Smallog + + +

    - -
    - - - -
    NameTypeDescription
    params - - -Object - - - - The given Items to filter
    +
    + Small logging util, with name/time prefix & log levels
    @@ -664,9 +568,9 @@
    Parameters:

    - View Source + View Source - wasc-worker/WascRT.ts, line 146 + Smallog.ts, line 53

    @@ -676,59 +580,27 @@
    Parameters:
    - - - - - - - - - - - - - -
    -
    -
    +
    - - -
    - -
    WebWorker-passable items
    - +
    +
    -
    - - -Object - -
    -
    - - -
    - - - - - - +
    +

    Methods

    +
    -

    - # +

    + # - myFetch(path, resType, owMime) → {Object} + getAllFiles(baseDir, subDir, arrayOfFiles) → {array}

    @@ -737,7 +609,7 @@

    - Small reusable fetch function, should work for local & web server files + list files recursively
    @@ -775,13 +647,13 @@

    Parameters:
    - path + baseDir -string +strring @@ -791,7 +663,7 @@
    Parameters:
    - file to request + start directory @@ -800,7 +672,7 @@
    Parameters:
    - resType + subDir @@ -816,7 +688,7 @@
    Parameters:
    - type of data to request (default = arraybuffer) + sub directory @@ -825,13 +697,13 @@
    Parameters:
    - owMime + arrayOfFiles -string +array @@ -841,7 +713,7 @@
    Parameters:
    - force-override mime-type (optional) + result files @@ -887,9 +759,9 @@
    Parameters:

    - View Source + View Source - wasc-worker/WascRT.ts, line 116 + offline/OfflinePlugin.js, line 48

    @@ -920,13 +792,13 @@
    Parameters:
    -
    XMLHttpRequest.response (converted to resType)
    +
    arrayOfFiles
    -Object +array
    @@ -960,7 +832,7 @@

    - Helper function + Helper function, resolves when html document is ready
    @@ -1042,8 +914,6 @@

    -
    resolve when site ready
    - @@ -100,14 +100,16 @@

    -

    we_utils

    +

    we_utils

    is a collection of utilities, mostly usefull when creating Wallpaper Engine Web Wallpapers with TypeScript / Webpack.

    I created this repository since I was previously copying back-and-forth lots of code between projects. Keeping track of this stuff manually is annoying...

    +

    Documentation

    Dependencies / Libraries

    Features / Contents

      @@ -119,11 +121,11 @@

      Features / Contents

    • "document.ready" shorthand
    • ReloadHelper for displaying a loading bar
    • Smallog Logging & Filtering
    • -
    • Stats.js definition file
    • WarnHelper for Seizure warnings
    • Wallpaper Engine iCUE Library (WEICUE)
    • Wallpaper Engine Wallpaper Adapter (WEWWA)
    • Worker-Loader definition file
    • +
    • Stats.js definition file

    Used by

    @@ -107,7 +107,7 @@

    offline/OfflinePlugin.js

    * schema for options object * @see {OfflinePlugin} */ -const offliineSchema = { +const offlineSchema = { type: 'object', properties: { staticdir: { @@ -179,7 +179,7 @@

    offline/OfflinePlugin.js

    * @param {offliineSchema} options */ constructor(options = {}) { - validate.validate(offliineSchema, options); + validate.validate(offlineSchema, options); this.options = options; } diff --git a/docs/scripts/prettify/Apache-License-2.0.txt b/docs/scripts/prettify/Apache-License-2.0.txt deleted file mode 100644 index d645695..0000000 --- a/docs/scripts/prettify/Apache-License-2.0.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/docs/scripts/prettify/lang-css.js b/docs/scripts/prettify/lang-css.js deleted file mode 100644 index 041e1f5..0000000 --- a/docs/scripts/prettify/lang-css.js +++ /dev/null @@ -1,2 +0,0 @@ -PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", -/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/docs/scripts/prettify/prettify.js b/docs/scripts/prettify/prettify.js deleted file mode 100644 index eef5ad7..0000000 --- a/docs/scripts/prettify/prettify.js +++ /dev/null @@ -1,28 +0,0 @@ -var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; -(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= -[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), -l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, -q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, -q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, -"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), -a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} -for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], -"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], -H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], -J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ -I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), -["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", -/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), -["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", -hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= -!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p th:last-child { border-right: 1px solid #ddd; } - -.ancestors, .attribs { color: #999; } -.ancestors a, .attribs a -{ - color: #999 !important; - text-decoration: none; -} - -.clear -{ - clear: both; -} - -.important -{ - font-weight: bold; - color: #950B02; -} - -.yes-def { - text-indent: -1000px; -} - -.type-signature { - color: #aaa; -} - -.name, .signature { - font-family: Consolas, Monaco, 'Andale Mono', monospace; -} - -.details { margin-top: 14px; border-left: 2px solid #DDD; } -.details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } -.details dd { margin-left: 70px; } -.details ul { margin: 0; } -.details ul { list-style-type: none; } -.details li { margin-left: 30px; padding-top: 6px; } -.details pre.prettyprint { margin: 0 } -.details .object-value { padding-top: 0; } - -.description { - margin-bottom: 1em; - margin-top: 1em; -} - -.code-caption -{ - font-style: italic; - font-size: 107%; - margin: 0; -} - -.source -{ - border: 1px solid #ddd; - width: 80%; - overflow: auto; -} - -.prettyprint.source { - width: inherit; -} - -.source code -{ - font-size: 100%; - line-height: 18px; - display: block; - padding: 4px 12px; - margin: 0; - background-color: #fff; - color: #4D4E53; -} - -.prettyprint code span.line -{ - display: inline-block; -} - -.prettyprint.linenums -{ - padding-left: 70px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.prettyprint.linenums ol -{ - padding-left: 0; -} - -.prettyprint.linenums li -{ - border-left: 3px #ddd solid; -} - -.prettyprint.linenums li.selected, -.prettyprint.linenums li.selected * -{ - background-color: lightyellow; -} - -.prettyprint.linenums li * -{ - -webkit-user-select: text; - -moz-user-select: text; - -ms-user-select: text; - user-select: text; -} - -.params .name, .props .name, .name code { - color: #4D4E53; - font-family: Consolas, Monaco, 'Andale Mono', monospace; - font-size: 100%; -} - -.params td.description > p:first-child, -.props td.description > p:first-child -{ - margin-top: 0; - padding-top: 0; -} - -.params td.description > p:last-child, -.props td.description > p:last-child -{ - margin-bottom: 0; - padding-bottom: 0; -} - -.disabled { - color: #454545; -} diff --git a/docs/wasc-worker_WascBuilderPlugin.js.html b/docs/wasc-worker_WascBuilderPlugin.js.html deleted file mode 100644 index caf1de3..0000000 --- a/docs/wasc-worker_WascBuilderPlugin.js.html +++ /dev/null @@ -1,322 +0,0 @@ - - - - - - - - - - wasc-worker/WascBuilderPlugin.js - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Source

    -

    wasc-worker/WascBuilderPlugin.js

    -
    - - - - - -
    -
    -
    /**
    -* @author hexxone / https://hexx.one
    -*
    -* @license
    -* Copyright (c) 2021 hexxone All rights reserved.
    -* Licensed under the GNU GENERAL PUBLIC LICENSE.
    -* See LICENSE file in the project root for full license information.
    -* @ignore
    -*/
    -
    -
    -const fs = require('fs');
    -const path = require('path');
    -
    -const asc = require('assemblyscript/bin/asc');
    -const validate = require('schema-utils');
    -const {RawSource} = require('webpack-sources');
    -const {Compilation} = require('webpack');
    -
    -const pluginName = 'WasmPlugin';
    -const outPath = path.resolve(__dirname, 'build');
    -
    -/**
    -* schema for options object
    -* @see {WascBuilderPlugin}
    -*/
    -const wascSchema = {
    -	type: 'object',
    -	properties: {
    -		production: {
    -			type: 'boolean',
    -		},
    -		relpath: {
    -			type: 'string',
    -		},
    -		extension: {
    -			type: 'string',
    -		},
    -		cleanup: {
    -			type: 'boolean',
    -		},
    -	},
    -};
    -
    -/**
    -* This is a webpack plugin
    -*/
    -class WascBuilderPlugin {
    -	options = {};
    -
    -	/**
    -	* Intializes the plugin in the webpack build process
    -	* @param {wascSchema} options
    -	*/
    -	constructor(options = {}) {
    -		validate.validate(wascSchema, options);
    -		this.options = options;
    -	}
    -
    -	/**
    -	* @ignore
    -	* Hook into the compilation process,
    -	* find all target files by a regex
    -	* then compile and add them to the webpack compiled files.
    -	* @param {Webpack.compiler} compiler object from webpack
    -	*/
    -	apply(compiler) {
    -		let addedOnce = false;
    -		// Specify the event hook to attach to
    -		compiler.hooks.thisCompilation.tap(pluginName,
    -			(compilation) => compilation.hooks.processAssets.tap({
    -				name: pluginName,
    -				stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
    -			}, async (assets) => {
    -				if (addedOnce) return;
    -				addedOnce = true;
    -				console.log('[' + pluginName + '] Gathering Infos....');
    -
    -				// add static files from folder
    -				const rPath = path.resolve(__dirname, this.options.relpath);
    -				const sFiles = this.getAllFiles(rPath, '');
    -
    -				// Parallel compiling
    -				await Promise.all(sFiles.map((sFile) => {
    -					// finally return Promise of module compilation
    -					return new Promise((resolve) => {
    -						// only compile if wasm name match regex
    -						const sName = sFile.replace(/^.*[\\\/]/, '');
    -						if (!sName.endsWith('.' + this.options.extension)) {
    -							resolve(false);
    -							return;
    -						}
    -
    -						console.info(`[${pluginName}] Compile ${this.options.production ? 'production' : 'debug'}: ${sName}`);
    -
    -						// change new file ext to ".wasm"
    -						let newName = sName.replace(/\.[^/.]+$/, '');
    -						if (!newName.endsWith('.wasm')) newName += '.wasm';
    -
    -						this.compileWasm(rPath + sFile, newName, this.options.production)
    -							.then(async ({normal, map}) => {
    -							// emit files into compilation
    -								if (normal) await compilation.emitAsset(newName, new RawSource(normal));
    -								if (map) await compilation.emitAsset(newName + '.map', new RawSource(map));
    -
    -								console.info('[' + pluginName + '] Success: ' + newName);
    -								resolve(true);
    -							});
    -					});
    -				}));
    -
    -				// finalize
    -				if (this.options.cleanup) await this.cleanUp();
    -
    -				console.info('[' + pluginName + '] finished.');
    -			}),
    -		);
    -	}
    -
    -
    -	/**
    -		* @ignore
    -		* list files recursively
    -		* @param {strring} baseDir start directory
    -		* @param {string} subDir sub directory
    -		* @param {array} arrayOfFiles result files
    -		* @return {array} arrayOfFiles
    -		*/
    -	getAllFiles(baseDir, subDir, arrayOfFiles) {
    -		const sub = baseDir + '/' + subDir;
    -		const files = fs.readdirSync(sub);
    -		arrayOfFiles = arrayOfFiles || [];
    -		files.forEach((file) => {
    -			const fle = subDir + '/' + file;
    -			if (fs.statSync(sub + '/' + file).isDirectory()) {
    -				arrayOfFiles = this.getAllFiles(baseDir, fle, arrayOfFiles);
    -			} else {
    -				arrayOfFiles.push(fle);
    -			}
    -		});
    -		return arrayOfFiles;
    -	}
    -
    -
    -	/**
    -		* @ignore
    -		* compile assemblyscript (typescript) module to wasm and return binary
    -		* @param {string} inputPath module to compile
    -		* @param {strring} newName target name
    -		* @param {boolean} production create symbols/map ?
    -		* @return {Promise} finished binary(s)
    -		*/
    -	compileWasm(inputPath, newName, production) {
    -		return new Promise((resolve) => {
    -			try {
    -				const newOut = path.resolve(outPath, newName);
    -
    -				asc.main([
    -					inputPath,
    -					'--extension', this.options.extension,
    -					'--binaryFile', newOut,
    -					'--measure',
    -					'--runtime', 'full',
    -					production ? '--optimize' : '--sourceMap',
    -				], (err) => {
    -					// let output = execSync('npm run asbuild', { cwd: __dirname });
    -					if (err) throw err;
    -					// none? -> read and resolve optimized.wasm string
    -					resolve(production ? {
    -						normal: fs.readFileSync(newOut),
    -					} : {
    -						normal: fs.readFileSync(newOut),
    -						map: fs.readFileSync(newOut + '.map'),
    -					});
    -				});
    -			} catch (ex) {
    -				console.warn('[' + pluginName + '] Compile Error!');
    -				console.error(ex);
    -			}
    -		});
    -	}
    -
    -	/**
    -		* delete all files in the output dir
    -		* @return {Promise} async finished event
    -		*/
    -	cleanUp() {
    -		console.info('[' + pluginName + '] Cleaning...');
    -		return new Promise((resolve) => {
    -			fs.readdir(outPath, (err, files) => {
    -				if (err) throw err;
    -				Promise.all(files.map((file) => {
    -					return new Promise((res) => {
    -						fs.unlink(path.join(outPath, file), (err) => {
    -							if (err) throw err;
    -							console.info('[' + pluginName + '] delete: ' + file);
    -							res();
    -						});
    -					});
    -				})).then(resolve);
    -			});
    -		});
    -	}
    -}
    -
    -module.exports = WascBuilderPlugin;
    -
    -
    -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - - diff --git a/docs/wasc-worker_WascInterface.ts.html b/docs/wasc-worker_WascInterface.ts.html deleted file mode 100644 index 5794ec9..0000000 --- a/docs/wasc-worker_WascInterface.ts.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - wasc-worker/WascInterface.ts - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Source

    -

    wasc-worker/WascInterface.ts

    -
    - - - - - -
    -
    -
    /**
    - * @author hexxone / https://hexx.one
    - *
    - * @license
    - * Copyright (c) 2021 hexxone All rights reserved.
    - * Licensed under the GNU GENERAL PUBLIC LICENSE.
    - * See LICENSE file in the project root for full license information.
    - */
    -
    -import {ResultObject, ASUtil} from '@assemblyscript/loader';
    -
    -/**
    - * The shared interface for loading a module
    - */
    -export class WascInterface implements ResultObject {
    -    module: WebAssembly.Module;
    -    instance: WebAssembly.Instance;
    -    exports: ASUtil;
    -    run: (func, ...params) => Promise<any>;
    -}
    -
    -
    -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - - diff --git a/docs/wasc-worker_WascLoader.ts.html b/docs/wasc-worker_WascLoader.ts.html deleted file mode 100644 index f2c35d1..0000000 --- a/docs/wasc-worker_WascLoader.ts.html +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - - - - - wasc-worker/WascLoader.ts - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Source

    -

    wasc-worker/WascLoader.ts

    -
    - - - - - -
    -
    -
    /**
    -* @author hexxone / https://hexx.one
    -*
    -* @license
    -* Copyright (c) 2021 hexxone All rights reserved.
    -* Licensed under the GNU GENERAL PUBLIC LICENSE.
    -* See LICENSE file in the project root for full license information.
    -*
    -* @description
    -* AssemblyScript module Loaders
    -*/
    -
    -import loader from '@assemblyscript/loader';
    -
    -import {makeRuntime, myFetch, ACTIONS, getTransferableParams, INITIAL_MEM} from './WascRT';
    -
    -import WascWorker from 'worker-loader!./Wasc';
    -import {WascInterface} from './WascInterface';
    -
    -/**
    -* Inline loads a compiled webassembly module.
    -* Basically the normal WebAssembly usage,
    -* just with api- and "run()"-compatibility
    -* @param {string} path compiled module path
    -* @param {number} initialMem initial memory size in kb
    -* @param {Object} options import Objects
    -* @return {Promise<WascInterface>} module
    -*/
    -export function LoadInline(path: string, initialMem: number = INITIAL_MEM, options: any = {}): Promise<WascInterface> {
    -	let ascExports: any;
    -	const memory = new WebAssembly.Memory({initial: initialMem});
    -	const staticImports = {
    -		env: {
    -			memory,
    -			logf(value) {
    -				console.log('F64: ' + value);
    -			},
    -			logi(value) {
    -				console.log('U32: ' + value);
    -			},
    -			logU32Array(ptr) {
    -				console.log(ascExports.getU32Array(ptr));
    -			},
    -			logF64Array(ptr) {
    -				console.log(ascExports.getF64Array(ptr));
    -			},
    -		},
    -	};
    -
    -	return new Promise(async (resolve) => {
    -		// get import object
    -		const {getImportObject} = options;
    -		const myImports = Object.assign({}, staticImports);
    -		if (getImportObject !== undefined) {
    -			Object.assign(myImports, getImportObject());
    -		}
    -
    -		const byteModule = await myFetch(path);
    -		const {module, instance, exports} = loader.instantiateSync(byteModule, myImports);
    -
    -		// get Exports
    -		const rtExports = makeRuntime(
    -			memory,
    -			exports.allocF64Array,
    -			exports.allocU32Array,
    -		);
    -
    -		// Add Helpers
    -		Object.assign(exports, {...rtExports});
    -		ascExports = exports;
    -
    -		/**
    -			* Run a function inside the worker.
    -			* @todo This is potentially dangerous due to eval!
    -			* @param {string} func stringified function to eval inside worker context
    -			* @param {Object} params Data to pass in
    -			* @return {Object} eval result
    -			*/
    -		function run(func, ...params) {
    -			return new Promise((res) => {
    -				const fun = new Function(`return ${func}`)();
    -				res(fun({
    -					module,
    -					instance,
    -					importObject: exports,
    -					params,
    -				}));
    -			});
    -		}
    -
    -		// we done here
    -		resolve({module, instance, exports, run} as any);
    -	});
    -}
    -
    -
    -/**
    -	* Creates a Worker, then loads a compiled webassembly module inside,
    -	* then creates a Promise-interface for all functions and additionally
    -	* wraps a "run" function inside the worker.
    -	* @param {string} source compiled module path
    -	* @param {Object} options import Objects
    -	* @return {Promise<WascInterface>} module
    -	*/
    -export function LoadWorker(source: string, options: any = {}): Promise<WascInterface> {
    -	// WRAP IN WORKER
    -	let currentId = 0;
    -	const promises = {};
    -	const worker = new WascWorker(options);
    -
    -	worker.onmessage = (e) => {
    -		const {id, result, action, payload} = e.data;
    -
    -		// COMPILE MODULE & RETURN EXPORTS
    -		if (action === ACTIONS.COMPILE_MODULE) {
    -			if (result === 0) {
    -				const {exports} = payload;
    -
    -				promises[id][0]({
    -
    -					// wrap the returned context/thread exports into promises
    -					exports: exports.reduce((acc, exp) => ({
    -						...acc,
    -						[exp]: (...params) => new Promise((...rest) => {
    -							promises[++currentId] = rest;
    -							worker.postMessage({
    -								id: currentId,
    -								action: ACTIONS.CALL_FUNCTION_EXPORT,
    -								payload: {
    -									func: exp,
    -									params,
    -								},
    -							}, getTransferableParams(params));
    -						}),
    -					}), {}),
    -
    -					// export context/thread run function
    -					run: (func, ...params) => new Promise((...rest) => {
    -						promises[++currentId] = rest;
    -						worker.postMessage({
    -							id: currentId,
    -							action: ACTIONS.RUN_FUNCTION,
    -							payload: {
    -								func: func.toString(),
    -								params,
    -							},
    -						}, getTransferableParams(params));
    -					}),
    -
    -				});
    -			} else if (result === 1) {
    -				promises[id][1](payload);
    -			}
    -
    -			// CALL FUNCTION
    -		} else if (
    -			action === ACTIONS.CALL_FUNCTION_EXPORT ||
    -				action === ACTIONS.RUN_FUNCTION
    -		) {
    -			promises[id][result](payload);
    -		}
    -
    -		promises[id] = null;
    -	};
    -
    -	return new Promise((...params) => {
    -		promises[++currentId] = [...params];
    -
    -		worker.postMessage({
    -			id: currentId,
    -			action: ACTIONS.COMPILE_MODULE,
    -			payload: source,
    -		});
    -	});
    -}
    -
    -
    -
    -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - - diff --git a/docs/wasc-worker_WascRT.ts.html b/docs/wasc-worker_WascRT.ts.html deleted file mode 100644 index 4326354..0000000 --- a/docs/wasc-worker_WascRT.ts.html +++ /dev/null @@ -1,277 +0,0 @@ - - - - - - - - - - wasc-worker/WascRT.ts - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Source

    -

    wasc-worker/WascRT.ts

    -
    - - - - - -
    -
    -
    /**
    -*
    -* @author hexxone / https://hexx.one
    -*
    -* @license
    -* Copyright (c) 2021 hexxone All rights reserved.
    -* Licensed under the GNU GENERAL PUBLIC LICENSE.
    -* See LICENSE file in the project root for full license information.
    -* @ignore
    -*/
    -
    -// TODO customize this according to needs
    -export const INITIAL_MEM = 4096;
    -
    -/**
    -* @ignore
    -* Creates the required internal runtime functions
    -* @param {WebAssembly.Memory} memory initial WebAssembly memory
    -* @param {Function} allocF64
    -* @param {Function} allocU32
    -* @return {Object} Runtime
    -*/
    -export function makeRuntime(memory: WebAssembly.Memory, allocF64, allocU32) {
    -	let mem; let F64; let U32;
    -	const cached = new WeakMap();
    -
    -	/**
    -	* Refreshes the local memory-representation.
    -	* This is usually required, after new memory was allocated in WebAssembly
    -	*/
    -	function refreshMemory() {
    -		if (mem !== memory.buffer) {
    -			mem = memory.buffer;
    -			U32 = new Uint32Array(mem);
    -			F64 = new Float64Array(mem);
    -		}
    -	}
    -
    -	/**
    -	* Allocates a new Float64Array with given data in WebAssembly
    -	* @param {Array} typedArray Data to pass in
    -	* @return {Object} allocated array pointer
    -	*/
    -	function newF64Array(typedArray) {
    -		let ptr;
    -		if (!cached.has(typedArray)) {
    -			ptr = allocF64(typedArray.length);
    -			refreshMemory();
    -			cached.set(typedArray, ptr);
    -		} else {
    -			refreshMemory();
    -			ptr = cached.get(typedArray);
    -		}
    -		const dataStart = (U32[ptr >>> 2] >>> 2) + 2;
    -		F64.set(typedArray, dataStart >>> 1);
    -		return ptr;
    -	}
    -
    -	/**
    -	* Copies and returns the float64array at given pointer
    -	* @param {Object} ptr allocated array pointer
    -	* @return {Float64Array} data
    -	*/
    -	function getF64Array(ptr) {
    -		refreshMemory();
    -		ptr >>>= 2;
    -
    -		const offset = (U32[ptr] >>> 2) + 2;
    -		const len = U32[ptr + 1];
    -
    -		return F64.subarray(offset, offset + len);
    -	}
    -
    -	/**
    -	* Create a new Uint32Array in WebAssembly
    -	* @param {Array} typedArray Data to pass in
    -	* @return {Object} allocated array pointer
    -	*/
    -	function newU32Array(typedArray) {
    -		let ptr;
    -		if (!cached.has(typedArray)) {
    -			ptr = allocU32(typedArray.length);
    -			refreshMemory();
    -			cached.set(typedArray, ptr);
    -		} else {
    -			refreshMemory();
    -			ptr = cached.get(typedArray);
    -		}
    -		const dataStart = (U32[ptr >>> 2] >>> 2) + 2;
    -		U32.set(typedArray, dataStart);
    -		return ptr;
    -	}
    -
    -	/**
    -	* Copies and returns the uint32array at given pointer
    -	* @param {Object} ptr allocated array pointer
    -	* @return {Uint32Array} data
    -	*/
    -	function getU32Array(ptr) {
    -		refreshMemory();
    -		ptr >>>= 2;
    -
    -		const offset = (U32[ptr] >>> 2) + 2;
    -		const len = U32[ptr + 1];
    -
    -		return U32.subarray(offset, offset + len);
    -	}
    -
    -	return {
    -		newF64Array,
    -		getF64Array,
    -		newU32Array,
    -		getU32Array,
    -	};
    -}
    -
    -/**
    -* Small reusable fetch function, should work for local & web server files
    -* @param {string} path file to request
    -* @param {string} resType type of data to request (default = arraybuffer)
    -* @param {string} owMime force-override mime-type (optional)
    -* @return {Object} XMLHttpRequest.response (converted to resType)
    -*/
    -export function myFetch(path: string, resType: string = 'arraybuffer', owMime?: string): Promise<any> {
    -	return new Promise((res) => {
    -		const request = new XMLHttpRequest();
    -		request.open('GET', path);
    -		if (owMime) request.overrideMimeType(owMime);
    -		request.responseType = resType as any;
    -		request.onload = () => {
    -			if (request.status != 200) console.error(request);
    -			res(request.response);
    -		};
    -		request.send();
    -	});
    -}
    -
    -/**
    -* Worker <-> Maincontext communication
    -*/
    -/* eslint-disable no-unused-vars */
    -export enum ACTIONS {
    -	COMPILE_MODULE = 0,
    -	CALL_FUNCTION_EXPORT = 1,
    -	RUN_FUNCTION = 2
    -}
    -
    -/**
    -* Filter out parameters for passing them into WebWorkers
    -* @param {Object} params The given Items to filter
    -* @return {Object} WebWorker-passable items
    -*/
    -export function getTransferableParams(...params: any): any {
    -	return params.filter((x) => (
    -		(x instanceof ArrayBuffer) ||
    -		(x instanceof MessagePort) ||
    -		(x instanceof ImageBitmap)
    -	)) || [];
    -}
    -
    -
    -
    -
    -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - - diff --git a/docs/wasc-worker_WascWorker.ts.html b/docs/wasc-worker_WascWorker.ts.html deleted file mode 100644 index b1fcb33..0000000 --- a/docs/wasc-worker_WascWorker.ts.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - - wasc-worker/WascWorker.ts - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Source

    -

    wasc-worker/WascWorker.ts

    -
    - - - - - -
    -
    -
    /**
    - * @author Matteo Basso @https://github.com/mbasso
    - * @author hexxone / https://hexx.one
    - *
    - * @license
    - * Copyright (c) 2021 hexxone All rights reserved.
    - * Licensed under the GNU GENERAL PUBLIC LICENSE.
    - * See LICENSE file in the project root for full license information.
    - *
    -*/
    -
    -import {WascInterface} from './WascInterface';
    -import {LoadInline, LoadWorker} from './WascLoader';
    -
    -/**
    - * Initializes a new WebAssembly instance.
    - * @param {string} source compiled .wasm module path
    - * @param {Object} options passed to the module init
    - * @param {boolean} useWorker use worker or inline
    - * @return {Promise<WascInterface>} the initialized context
    - */
    -export default function WascWorker(source: string, options: any = {}, useWorker: boolean = true): Promise<WascInterface> {
    -	return new Promise(async (resolve) => {
    -		// initialize the actual module
    -		const promiseMe = (useWorker && Worker) ? LoadWorker : LoadInline;
    -		const result = await promiseMe(source, options);
    -
    -		// return the freshly initialized module
    -		resolve(result);
    -	});
    -}
    -
    -
    -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - - diff --git a/docs/wasc-worker_index.ts.html b/docs/wasc-worker_index.ts.html deleted file mode 100644 index 8c12f2a..0000000 --- a/docs/wasc-worker_index.ts.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - - wasc-worker/index.ts - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Source

    -

    wasc-worker/index.ts

    -
    - - - - - -
    -
    -
    /**
    - * @author Matteo Basso @https://github.com/mbasso
    - * @author hexxone / https://hexx.one
    - *
    - * @license
    - * Copyright (c) 2021 hexxone All rights reserved.
    - * Licensed under the GNU GENERAL PUBLIC LICENSE.
    - * See LICENSE file in the project root for full license information.
    - *
    -*/
    -
    -import {WascInterface} from './WascInterface';
    -import {LoadInline, LoadWorker} from './WascLoader';
    -
    -/**
    - * Initializes a new WebAssembly instance.
    - * @param {string} source compiled .wasm module path
    - * @param {Object} options passed to the module init
    - * @param {boolean} useWorker use worker or inline
    - * @return {Promise<WascInterface>} the initialized context
    - */
    -export default function WascInit(source: string, options: any = {}, useWorker: boolean = true): Promise<WascInterface> {
    -	return new Promise(async (resolve) => {
    -		// initialize the actual module
    -		const promiseMe = (useWorker && Worker) ? LoadWorker : LoadInline;
    -		const result = await promiseMe(source, options);
    -
    -		// return the freshly initialized module
    -		resolve(result);
    -	});
    -}
    -
    -
    -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - - diff --git a/jsdoc.json b/jsdoc.json index 4c26639..5af53e2 100644 --- a/jsdoc.json +++ b/jsdoc.json @@ -1,6 +1,6 @@ { "opts": { - "template": "./../../node_modules/better-docs", + "template": "./src/wasc-worker/better-docs", "encoding": "utf8", "destination": "./docs/", "recurse": true, @@ -8,12 +8,12 @@ "access": "all" }, "plugins": [ - "./../../node_modules/better-docs/typescript" + "./src/wasc-worker/better-docs/typescript" ], "recurseDepth": 10, "source": { "include": ["./src"], - "exclude": ["./src/wasc-worker/node_modules"], + "exclude": ["./src/wasc-worker/"], "includePattern": ".+\\.(jsx|js|tsx|ts)$", "excludePattern": "(^|\\/|\\\\)_" }, diff --git a/src/CComponent.ts b/src/CComponent.ts index c4577cd..54a45fa 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -16,12 +16,14 @@ import {Smallog} from './Smallog'; export class CComponent { private needsUpdate = false; - /** main Settings, need to be overwritten with Specific settings - * @see {CSettings} - */ + /** + * main Settings, need to be overwritten with Specific settings + */ public settings: CSettings = null; - /* Important: Append your child objects, for settings to be applied correctly! */ + /** + * Important: Append your child objects, for settings to be applied correctly! + */ public children: CComponent[] = []; /** diff --git a/src/CSettings.ts b/src/CSettings.ts index 760198c..6e0823c 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -31,7 +31,7 @@ export class CSettings { this[key] = castedValue; return true; } else { - Smallog.Error('CSettings Error: invalid type on: \'' + key + + Smallog.error('CSettings Error: invalid type on: \'' + key + '\'. Is: \'' + typeof this[key] + '\', applied: \'' + typeof castedValue + '\''); } } diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index d80be51..a18c521 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -41,6 +41,7 @@ export class ReloadHelper extends CComponent { /** * Make custom style + * @ignore */ private injectCSS() { const st = document.createElement('style'); @@ -89,6 +90,7 @@ export class ReloadHelper extends CComponent { /** * Make custom html elements + * @ignore */ private injectHTML() { const outer = document.createElement('div'); diff --git a/src/Smallog.ts b/src/Smallog.ts index 4008e6b..df1e223 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -9,9 +9,15 @@ */ /* eslint-disable no-unused-vars */ -/* eslint-disable require-jsdoc */ -export function traceCall(def: string, depth: number = 3) { +/** + * trace exception calls + * @param {string} def error message + * @param {number} depth which call to pick + * @return {string} + * @ignore + */ +function traceCall(def: string, depth: number = 3) { try { throw new Error('TraceCall()'); } catch (e) { @@ -24,48 +30,101 @@ export function traceCall(def: string, depth: number = 3) { return def; } +/** + * @see {Smallog} + */ export enum LogLevel { + /** + * Print only error messages + */ Error = 0, + /** + * Print error and info messages + */ Info = 1, + /** + * Print all messages + */ Debug = 2 } +/** + * Small logging util, with name/time prefix & log levels + */ export module Smallog { let logLevel: LogLevel = LogLevel.Debug; // todo level Info for release let preFix: string = '[Smallog] '; let printTime: boolean = false; - export function GetLevel() { + /** + * get logging output level + * @return {LogLevel} current + */ + export function getLevel() { return logLevel; } + /** + * set logging output level + * @param {LogLevel} level new + */ export function setLevel(level: LogLevel) { logLevel = level; } + /** + * set logging prefix + * @param {string} pre + */ export function setPrefix(pre: string) { preFix = pre; } - export function SetPrintTime(print: boolean) { + /** + * set time prefix + * @param {boolean} print + */ + export function setPrintTime(print: boolean) { printTime = print; } - export function Error(msg: string, hdr: string = preFix) { + /** + * print error message + * @param {string} msg log + * @param {string} hdr overwrite header + */ + export function error(msg: string, hdr: string = preFix) { log(console.error, msg, traceCall(hdr)); } + /** + * print info message + * @param {string} msg log + * @param {string} hdr overwrite header + */ export function info(msg: string, hdr: string = preFix) { if (logLevel >= 2) hdr = traceCall(hdr); if (logLevel >= 1) log(console.info, msg, hdr); } + /** + * print debug message + * @param {string} msg log + * @param {string} hdr overwrite header + */ export function debug(msg: string, hdr: string = preFix) { if (logLevel >= 2) log(console.debug, msg, traceCall(hdr)); } - function log(call: any, msg: string, hdr: string) { + /** + * internal logging function + * @param {Function} call log callback + * @param {string} msg text to print + * @param {string} hdr header to include + * @ignore + */ + function log(call: (...data: any) => void, msg: string, hdr: string) { let m = msg; if (printTime) m = ('[' + new Date().toLocaleString() + '] ') + m; call(hdr + m); diff --git a/src/Util.ts b/src/Util.ts index 1e4dacf..703ea18 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -11,8 +11,8 @@ */ /** -* Helper function -* @return {Promise} resolve when site ready +* Helper function, resolves when html document is ready +* @return {Promise} */ export function waitReady() { return new Promise((resolve) => { diff --git a/src/WEAS.ts b/src/WEAS.ts index d8c705c..270458f 100644 --- a/src/WEAS.ts +++ b/src/WEAS.ts @@ -7,7 +7,6 @@ * See LICENSE file in the project root for full license information. */ -import {ASUtil} from '@assemblyscript/loader'; import {CComponent} from './CComponent'; import {CSettings} from './CSettings'; import {waitReady} from './Util'; @@ -114,6 +113,7 @@ export class WEAS extends CComponent { /** * initializes audio processing pipeline * and starts listening on audio data + * @ignore */ private async realInit() { // if wallpaper engine context given, listen @@ -135,7 +135,7 @@ export class WEAS extends CComponent { // Smallog.debug('Get Audio Data!'); // check basic if (!self.settings.audioprocessing || audioArray == null || audioArray.length != DAT_LEN) { - Smallog.Error('audioListener: received invalid audio data array: ' + JSON.stringify([audioArray.length || null, audioArray])); + Smallog.error('audioListener: received invalid audio data array: ' + JSON.stringify([audioArray.length || null, audioArray])); return; } // check nulls @@ -155,19 +155,18 @@ export class WEAS extends CComponent { self.inBuff.set(audioArray); // WRAP IN isolated Function ran inside worker - run(({module, instance, importObject, params}) => { - const {exports} = instance; + run(({module, instance, exports, params}) => { + const ex = instance.exports as any; const {data} = params[0]; - const io = importObject as ASUtil; // set audio data directly in module memory - io.__getFloat64ArrayView(exports.inputData).set(data); + exports.__getFloat64ArrayView(ex.inputData).set(data); // trigger processing processing - exports.update(); + ex.update(); // get copy of updated Data & Properties const r = { // => result - data: new Float64Array(io.__getFloat64ArrayView(exports.outputData)), - props: new Float64Array(io.__getFloat64ArrayView(exports.audioProps)), + data: new Float64Array(exports.__getFloat64ArrayView(ex.outputData)), + props: new Float64Array(exports.__getFloat64ArrayView(ex.audioProps)), }; return r; }, // params passed to worker @@ -194,6 +193,7 @@ export class WEAS extends CComponent { * converts calculated output property number-array to string-associative-array * @param {ArrayLike} dProps processed properties * @return {Object} + * @ignore */ private getProps(dProps: ArrayLike) { const keys = Object.keys(PropIDs); @@ -224,11 +224,10 @@ export class WEAS extends CComponent { } // isolated Function running inside worker - run(({module, instance, importObject, params}) => { - const {exports} = instance; + run(({module, instance, exports, params}) => { + const ex = instance.exports as any; const {data} = params[0]; - const io = importObject as ASUtil; - io.__getFloat64ArrayView(exports.audioSettings).set(data); + exports.__getFloat64ArrayView(ex.audioSettings).set(data); }, // Data passed to worker { diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 7707a5b..4327ccc 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -60,7 +60,6 @@ export class WEICUE extends CComponent { // runtime values public settings: CUESettings = new CUESettings(); - public isAvailable: boolean = false; public PAUSED: boolean = false; @@ -91,6 +90,7 @@ export class WEICUE extends CComponent { /** * style for iCue messages, preview and helper + * @ignore */ private injectCSS() { const st = document.createElement('style'); @@ -136,6 +136,7 @@ export class WEICUE extends CComponent { /** * Prepare html elements + * @ignore */ private injectHTML() { // create container @@ -322,6 +323,7 @@ export class WEICUE extends CComponent { * show a message by icue * @param {string} msg * @param {boolean} waiting + * @ignore */ private icueMessage(msg: string, waiting :boolean = false) { Smallog.debug('MSG: ' + msg, ClassName); @@ -342,6 +344,7 @@ export class WEICUE extends CComponent { * helper * @param {boolean} inPx suffix "px" string to number (allows direct css use) * @return {Object} area + * @ignore */ private getArea(inPx = false) { const sett = this.settings; @@ -363,6 +366,7 @@ export class WEICUE extends CComponent { * convert data for icue * @param {ImageData} imageData * @return {string} + * @ignore */ private getEncodedCanvasImageData(imageData: ImageData) { const colorArray = []; @@ -380,6 +384,7 @@ export class WEICUE extends CComponent { * @param {HTMLCanvasElement} canvas * @param {CanvasRenderingContext2D} ctx * @param {number} blur + * @ignore */ private gBlurCanvas(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, blur: number) { let sum = 0; @@ -405,6 +410,7 @@ export class WEICUE extends CComponent { /** * Show waiting message and init canvas + * @ignore */ private init() { const sett = this.settings; @@ -457,6 +463,7 @@ export class WEICUE extends CComponent { /** * will initialize ICUE api & usage * @param {number} count Retries (will stop at 100) + * @ignore */ private initCUE(count) { // wait for plugins @@ -485,6 +492,7 @@ export class WEICUE extends CComponent { /** * do the thing... + * @ignore */ private updateFrame() { const sett = this.settings; @@ -525,9 +533,9 @@ export class WEICUE extends CComponent { } /** - * copy main canvas portion to our helper - * @param {HTMLCanvasElementq} mainCanvas - */ + * copy main canvas portion to our helper + * @param {HTMLCanvasElementq} mainCanvas + */ public updateCanvas(mainCanvas: HTMLCanvasElement) { const sett = this.settings; if (!this.isAvailable || !mainCanvas || sett.icue_mode == 0 || this.icueDevices.length < 1) return; diff --git a/src/WEWA.ts b/src/WEWA.ts index 0b6bc0b..ceeb98c 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -70,7 +70,7 @@ const DefLang = 'de-de'; * - cf longer cache policy (2d?) * - iniitialize, else => do nothing - * @param {Function} finished Callback - */ + * Check if we are running in Web-Mode + * if yes => iniitialize, else => do nothing + * @param {Function} finished Callback for initializing the wallpaper + */ constructor(finished) { if (window['wallpaperRegisterAudioListener']) { Smallog.info('detected wallpaper engine => Standby.', LogHead); @@ -137,12 +137,13 @@ export class WEWWA { } /** - * Initialize the Web Adapter - */ + * Initialize the Web Adapter + * @ignore + */ private init() { myFetch('project.json', 'json').then((proj) => { if (proj.type != 'web') { - Smallog.Error('Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...', LogHead); + Smallog.error('Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...', LogHead); return; } this.project = proj; @@ -155,8 +156,9 @@ export class WEWWA { } /** - * Load last settings from localStorage - */ + * Load last settings from localStorage + * @ignore + */ private loadStorage() { const props = this.project.general.properties; const last = localStorage.getItem('wewwaLastProps'); @@ -172,8 +174,9 @@ export class WEWWA { } /** - * CSS Insertion - */ + * CSS Insertion + * @ignore + */ private addStyle() { const st = document.createElement('style'); // precalculation @@ -303,6 +306,7 @@ export class WEWWA { /** * HTML Creation * @param {string} lang WE language + * @ignore */ private addMenu(lang) { const self = this; @@ -340,6 +344,7 @@ export class WEWWA { * Adds the Menu Icon * @param {Function} ce CreateElement * @param {Element} menu + * @ignore */ private addMenuIcon(ce: (e: any) => any, menu = this.htmlMenu) { const icon = this.htmlIcon = ce('div'); @@ -364,14 +369,15 @@ export class WEWWA { } /** - * Adds the actual Wallpaper Props as HTML - * @param {Function} ce Create Element wrapper - * @param {Object} proj project - * @param {object} self this - * @param {string} lang - * @param {object} props - * @param {Element} menu - */ + * Adds the actual Wallpaper Props as HTML + * @param {Function} ce Create Element wrapper + * @param {Object} proj project + * @param {object} self this + * @param {string} lang + * @param {object} props + * @param {Element} menu + * @ignore + */ private addMenuSettings(ce: (e: any) => any, proj: any, self: this, lang: string, props: any, menu = this.htmlMenu) { const tbl = ce('table'); tbl.innerHTML = ' '; @@ -446,9 +452,10 @@ export class WEWWA { } /** - * Add missing default localization strings - * @param {Object} local - */ + * Add missing default localization strings + * @param {Object} local + * @ignore + */ private mergeLocals(local: any) { const locDefs = { 'ui_browse_properties_scheme_color': 'Scheme color', @@ -464,10 +471,11 @@ export class WEWWA { } /** - * Adds the Footer Link to the Menu - * @param {Function} ce create element - * @param {Element} menu - */ + * Adds the Footer Link to the Menu + * @param {Function} ce create element + * @param {Element} menu + * @ignore + */ private addMenuFooter(ce: (e: any) => any, menu = this.htmlMenu) { const preFoot = ce('div'); preFoot.innerHTML = '
    '; @@ -504,8 +512,9 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Add Language Menu - */ + * Add Language Menu + * @ignore + */ private makeMenuLocalization(ce: (e: any) => any, lang, local, props) { const self = this; // add html struct @@ -558,8 +567,9 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Add Audio Menu - */ + * Add Audio Menu + * @ignore + */ private addMenuAudio(ce: (e: any) => any, tblBody: any) { // audio input methods const row = ce('tr'); @@ -647,8 +657,9 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Add preview Image, Title and Link - */ + * Add preview Image, Title and Link + * @ignore + */ private addMenuHeader(ce: (e: any) => any, proj: any, menu = this.htmlMenu) { const preview = ce('img'); preview.setAttribute('src', proj.preview); @@ -666,8 +677,9 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Create an HTML Menu Item from project json property - */ + * Create an HTML Menu Item from project json property + * @ignore + */ private createItem(prop, itm) { if (!itm.type || itm.type == 'hidden') return null; const self = this; @@ -765,7 +777,7 @@ export class WEWWA { break; default: - Smallog.Error('unkown setting type: ' + itm.type, LogHead); + Smallog.error('unkown setting type: ' + itm.type, LogHead); break; } @@ -805,16 +817,16 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Callback for UI-Settings changes - * Will apply them to the storage and running wallaper. - */ + * Callback for UI-Settings changes + * Will apply them to the storage and running wallaper. + */ public setProperty(prop, elm) { // get the type and apply the value const props = this.project.general.properties; // check for legit setting... if (!props[prop]) { - Smallog.Error('SetProperty name not found: ' + prop, LogHead); + Smallog.error('SetProperty name not found: ' + prop, LogHead); return; } @@ -844,11 +856,11 @@ export class WEWWA { if (elm.name.includes('_out_')) { const inpt: any = document.querySelector('#wewwa_' + prop); if (inpt) inpt.value = elm.value; - else Smallog.Error('Slider not found: ' + prop, LogHead); + else Smallog.error('Slider not found: ' + prop, LogHead); } else { const slide: any = document.querySelector('#wewwa_out_' + prop); if (slide) slide.value = elm.value; - else Smallog.Error('Numericupdown not found: ' + prop, LogHead); + else Smallog.error('Numericupdown not found: ' + prop, LogHead); } case 'combo': case 'textinput': @@ -859,10 +871,11 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * will load the given file and return it as dataURL. - * this way we can easily store whole files in the configuration & localStorage. - * its not safe that this works with something else than image files. - */ + * will load the given file and return it as dataURL. + * this way we can easily store whole files in the configuration & localStorage. + * its not safe that this works with something else than image files. + * @ignore + */ private loadXHRSaveLocal(url, resCall) { myFetch(url, 'blob').then((resp) => { // Read out file contents as a Data URL @@ -875,8 +888,8 @@ export class WEWWA { } /** - * Show or hide menu items based on eval condition - */ + * Show or hide menu items based on eval condition + */ public evaluateSettings() { const wewwaProps = this.project.general.properties; localStorage.setItem('wewwaLastProps', JSON.stringify(wewwaProps)); @@ -909,7 +922,7 @@ export class WEWWA { try { visible = eval(cprop) == true; } catch (e) { - Smallog.Error('Error: (' + cprop + ') for: ' + p + ' => ' + e, LogHead); + Smallog.error('Error: (' + cprop + ') for: ' + p + ' => ' + e, LogHead); } } @@ -945,8 +958,8 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Send one or more properties to the Wallpaper - */ + * Send one or more properties to the Wallpaper + */ public applyProp(prop) { const wpl = window['wallpaperPropertyListener']; if (wpl && wpl.applyUserProperties) { @@ -956,8 +969,8 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Send paused-status to the Wallpaper - */ + * Send paused-status to the Wallpaper + */ public setPaused(val: boolean) { const wpl = window['wallpaperPropertyListener']; if (this.isPaused == val) return; @@ -995,8 +1008,9 @@ export class WEWWA { // ------------------------------------- /** - * Request microphone from browser - */ + * Request microphone from browser + * @ignore + */ private requestMicrophone() { navigator.mediaDevices.getUserMedia({ audio: true, @@ -1013,7 +1027,7 @@ export class WEWWA { this.source.connect(this.analyser); this.startAudioInterval(); }).catch((err) => { - Smallog.Error(err, LogHead); + Smallog.error(err, LogHead); if (location.protocol != 'https:') { const r = confirm('Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS for this feature to work! Press \'ok\'/\'yes\' to get redirected to HTTPS and try again.'); if (r) window.location.href = window.location.href.replace('http', 'https'); @@ -1023,11 +1037,12 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * html5 audio analyser gives us mono data from 0(bass) to 128(treble) - * however, wallpaper engine expects stereo data in following format: - * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) - * so we do some array transformation... and divide by 255 (8bit-uint becomes float) - */ + * html5 audio analyser gives us mono data from 0(bass) to 128(treble) + * however, wallpaper engine expects stereo data in following format: + * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) + * so we do some array transformation... and divide by 255 (8bit-uint becomes float) + * @ignore + */ private convertAudio(data) { const stereo = []; let sIdx = 0; @@ -1040,8 +1055,9 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Start the audio processing & analyzer - */ + * Start the audio processing & analyzer + * @ignore + */ private initiateAudio(data) { // clear up this.stopAudioInterval(); @@ -1068,15 +1084,16 @@ export class WEWWA { } /** - * Start the processing loop - */ + * Start the processing loop + * @ignore + */ private startAudioInterval() { const data = new Uint8Array(128); // 33ms ~~ 30fps this.audioInterval = window.setInterval(() => { if (this.audioCallback == null) { this.stopAudioInterval(); - Smallog.Error('no AudioCallback!', LogHead); + Smallog.error('no AudioCallback!', LogHead); return; } this.analyser.getByteFrequencyData(data); @@ -1088,8 +1105,8 @@ export class WEWWA { } /** - * Stop the processing loop - */ + * Stop the processing loop + */ public stopAudioInterval() { window['persistAudioStream'] = null; document.getElementById('wewwaAudioInput').setAttribute('value', ''); diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 595ed0a..5d54c9b 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -22,6 +22,7 @@ const ELM_ID = 'triggerwarn'; /** * @TODO test getting text +* @extends {CSettings} */ class WarnSettings extends CSettings { seizure_warning: boolean = true; @@ -32,6 +33,7 @@ class WarnSettings extends CSettings { /** * Seizure warning display +* @extends {CComponent} */ export class WarnHelper extends CComponent { public settings: WarnSettings = new WarnSettings(); @@ -55,6 +57,7 @@ export class WarnHelper extends CComponent { /** * Make custom style + * @ignore */ private injectCSS() { const st = document.createElement('style'); @@ -76,6 +79,7 @@ export class WarnHelper extends CComponent { /** * Make custom html + * @ignore */ private injectHTML() { this.element = document.createElement('div'); @@ -85,6 +89,7 @@ export class WarnHelper extends CComponent { /** * Set html text value + * @ignore */ private setText() { this.element.innerHTML = this.settings.seizure_text.replace('\r\n', '
    '); diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index 01ed727..afb9866 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -49,11 +49,11 @@ export module OfflineHelper { const workerPath = `${worker}?name=${name}&jsonPath=${oFile}`; await navigator.serviceWorker.register(workerPath, {scope: '/'}) .then(() => Smallog.info('service-worker registration complete.', LogHead), - () => Smallog.Error('service-worker registration failure.', LogHead)) + () => Smallog.error('service-worker registration failure.', LogHead)) .then(() => resolve(true)); return true; } else { - Smallog.Error(LogErrS, LogHead); + Smallog.error(LogErrS, LogHead); resolve(false); } }); @@ -73,7 +73,7 @@ export module OfflineHelper { resolve(true); }); } else { - Smallog.Error(LogErrS, LogHead); + Smallog.error(LogErrS, LogHead); resolve(false); } }); diff --git a/src/offline/OfflinePlugin.js b/src/offline/OfflinePlugin.js index 619ebf4..4a65fd2 100644 --- a/src/offline/OfflinePlugin.js +++ b/src/offline/OfflinePlugin.js @@ -20,7 +20,7 @@ const pluginName = 'OfflinePlugin'; * schema for options object * @see {OfflinePlugin} */ -const offliineSchema = { +const offlineSchema = { type: 'object', properties: { staticdir: { @@ -92,7 +92,7 @@ class OfflinePlugin { * @param {offliineSchema} options */ constructor(options = {}) { - validate.validate(offliineSchema, options); + validate.validate(offlineSchema, options); this.options = options; } diff --git a/src/wasc-worker b/src/wasc-worker index f7350c1..1ff845d 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit f7350c1964b775d1dfb65469580da6deae78174f +Subproject commit 1ff845da6c841ceb94ab970dfa0ca3cd559a5432 From 3d5d1924565b4602b6bb865a5be58c99cda7952d Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Wed, 2 Jun 2021 13:25:03 +0200 Subject: [PATCH 30/76] move three utils; add FPStats; refactoring; docs --- README.md | 5 +- Stats.js | 147 - docs/BasePass.html | 203 ++ docs/BaseShader.html | 596 ++++ docs/BlendShader.html | 692 +++++ docs/BlurShader.html | 692 +++++ docs/CComponent.html | 28 +- docs/CComponent.ts.html | 21 +- docs/CSettings.html | 10 +- docs/CSettings.ts.html | 19 +- docs/CUESettings.html | 956 +++---- docs/ChromaticShader.html | 692 +++++ docs/CopyShader.html | 692 +++++ docs/EffectComposer.html | 1430 ++++++++++ docs/FPSettings.html | 479 ++++ docs/FPSta.ts.html | 326 +++ docs/FPStats.html | 1431 ++++++++++ docs/FXAAShader.html | 692 +++++ docs/FractalMirrorShader.html | 692 +++++ docs/FullScreenHelper.html | 713 +++++ docs/LUTShader.html | 692 +++++ docs/LUTShaderNearest.html | 721 +++++ docs/LuminosityHighPassShader.html | 692 +++++ docs/OfflinePlugin.html | 2 +- docs/ReloadHelper.html | 151 +- docs/ReloadHelper.ts.html | 43 +- docs/ReloadSettings.html | 2 +- docs/RenderPass.html | 963 +++++++ docs/ShaderPass.html | 878 ++++++ docs/Smallog.ts.html | 121 +- docs/UnrealBloomPass.html | 1255 +++++++++ docs/Util.ts.html | 20 +- docs/WEAS.html | 469 +++- docs/WEAS.ts.html | 225 +- docs/WEASettings.html | 1262 ++++----- docs/WEAudio.html | 196 ++ docs/WEICUE.html | 24 +- docs/WEICUE.ts.html | 230 +- docs/WEWA.ts.html | 40 +- docs/WEWWA.html | 14 +- docs/WarnHelper.html | 28 +- docs/WarnHelper.ts.html | 65 +- docs/WarnSettings.html | 947 ++++--- docs/XRHelper.html | 1261 +++++++++ docs/XRSettings.html | 479 ++++ docs/global.html | 2423 ++++++++++------- docs/index.html | 7 +- docs/offline_OfflineHelper.ts.html | 203 ++ docs/offline_OfflinePlugin.js.html | 2 +- docs/three_EffectComposer.ts.html | 419 +++ docs/three_XRHelper.ts.html | 262 ++ docs/three_pass_BasePass.ts.html | 143 + docs/three_pass_FullScreenHelper.ts.html | 172 ++ docs/three_pass_RenderPass.ts.html | 217 ++ docs/three_pass_ShaderPass.ts.html | 218 ++ docs/three_pass_UnrealBloomPass.ts.html | 506 ++++ docs/three_shader_BaseShader.ts.html | 161 ++ docs/three_shader_BlendShader.ts.html | 166 ++ docs/three_shader_BlurShader.ts.html | 190 ++ docs/three_shader_ChromaticShader.ts.html | 176 ++ docs/three_shader_CopyShader.ts.html | 165 ++ docs/three_shader_FXAAShader.ts.html | 992 +++++++ docs/three_shader_FractalMirrorShader.ts.html | 188 ++ docs/three_shader_LUTShader.ts.html | 198 ++ docs/three_shader_LUTShaderNearest.ts.html | 138 + ...ee_shader_LuminosityHighPassShader.ts.html | 178 ++ index.ts | 26 + src/CComponent.ts | 19 +- src/CSettings.ts | 17 +- src/FPSta.ts | 210 ++ src/ReloadHelper.ts | 41 +- src/Smallog.ts | 119 +- src/Stats.ts | 38 - src/Util.ts | 18 +- src/WEAS.ts | 223 +- src/WEAS.wasm.asc | 73 +- src/WEICUE.ts | 228 +- src/WEWA.ts | 38 +- src/WarnHelper.ts | 63 +- src/offline/OfflineHelper.ts | 104 +- src/three/EffectComposer.ts | 303 +++ src/three/XRHelper.ts | 146 + src/three/index.ts | 26 + src/three/pass/BasePass.ts | 27 + src/three/pass/FullScreenHelper.ts | 56 + src/three/pass/RenderPass.ts | 101 + src/three/pass/ShaderPass.ts | 102 + src/three/pass/UnrealBloomPass.ts | 390 +++ src/three/shader/BaseShader.ts | 45 + src/three/shader/BlendShader.ts | 50 + src/three/shader/BlurShader.ts | 74 + src/three/shader/ChromaticShader.ts | 60 + src/three/shader/CopyShader.ts | 49 + src/three/shader/FXAAShader.ts | 876 ++++++ src/three/shader/FractalMirrorShader.ts | 72 + src/three/shader/LUTShader.ts | 82 + src/three/shader/LUTShaderNearest.ts | 22 + src/three/shader/LuminosityHighPassShader.ts | 62 + src/wasc-worker | 2 +- 99 files changed, 28547 insertions(+), 3635 deletions(-) delete mode 100644 Stats.js create mode 100644 docs/BasePass.html create mode 100644 docs/BaseShader.html create mode 100644 docs/BlendShader.html create mode 100644 docs/BlurShader.html create mode 100644 docs/ChromaticShader.html create mode 100644 docs/CopyShader.html create mode 100644 docs/EffectComposer.html create mode 100644 docs/FPSettings.html create mode 100644 docs/FPSta.ts.html create mode 100644 docs/FPStats.html create mode 100644 docs/FXAAShader.html create mode 100644 docs/FractalMirrorShader.html create mode 100644 docs/FullScreenHelper.html create mode 100644 docs/LUTShader.html create mode 100644 docs/LUTShaderNearest.html create mode 100644 docs/LuminosityHighPassShader.html create mode 100644 docs/RenderPass.html create mode 100644 docs/ShaderPass.html create mode 100644 docs/UnrealBloomPass.html create mode 100644 docs/WEAudio.html create mode 100644 docs/XRHelper.html create mode 100644 docs/XRSettings.html create mode 100644 docs/offline_OfflineHelper.ts.html create mode 100644 docs/three_EffectComposer.ts.html create mode 100644 docs/three_XRHelper.ts.html create mode 100644 docs/three_pass_BasePass.ts.html create mode 100644 docs/three_pass_FullScreenHelper.ts.html create mode 100644 docs/three_pass_RenderPass.ts.html create mode 100644 docs/three_pass_ShaderPass.ts.html create mode 100644 docs/three_pass_UnrealBloomPass.ts.html create mode 100644 docs/three_shader_BaseShader.ts.html create mode 100644 docs/three_shader_BlendShader.ts.html create mode 100644 docs/three_shader_BlurShader.ts.html create mode 100644 docs/three_shader_ChromaticShader.ts.html create mode 100644 docs/three_shader_CopyShader.ts.html create mode 100644 docs/three_shader_FXAAShader.ts.html create mode 100644 docs/three_shader_FractalMirrorShader.ts.html create mode 100644 docs/three_shader_LUTShader.ts.html create mode 100644 docs/three_shader_LUTShaderNearest.ts.html create mode 100644 docs/three_shader_LuminosityHighPassShader.ts.html create mode 100644 index.ts create mode 100644 src/FPSta.ts delete mode 100644 src/Stats.ts create mode 100644 src/three/EffectComposer.ts create mode 100644 src/three/XRHelper.ts create mode 100644 src/three/index.ts create mode 100644 src/three/pass/BasePass.ts create mode 100644 src/three/pass/FullScreenHelper.ts create mode 100644 src/three/pass/RenderPass.ts create mode 100644 src/three/pass/ShaderPass.ts create mode 100644 src/three/pass/UnrealBloomPass.ts create mode 100644 src/three/shader/BaseShader.ts create mode 100644 src/three/shader/BlendShader.ts create mode 100644 src/three/shader/BlurShader.ts create mode 100644 src/three/shader/ChromaticShader.ts create mode 100644 src/three/shader/CopyShader.ts create mode 100644 src/three/shader/FXAAShader.ts create mode 100644 src/three/shader/FractalMirrorShader.ts create mode 100644 src/three/shader/LUTShader.ts create mode 100644 src/three/shader/LUTShaderNearest.ts create mode 100644 src/three/shader/LuminosityHighPassShader.ts diff --git a/README.md b/README.md index 7f94e6d..6820a0f 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,12 @@ Keeping track of this stuff manually is annoying... ### Dependencies / Libraries -- [typescript](https://www.typescriptlang.org/) for typization +- [TypeScript](https://www.typescriptlang.org/) for typization - [three.js](https://threejs.org/) & Examples for webgl rendering +- [WebAssembly](https://webassembly.org/) for more efficient processing +- [AssemblyScript](https://www.assemblyscript.org/) for compiling "ASC" -> "WASM" - [wasc-worker](https://github.com/hexxone/wasc-worker) for ez AssemblyScript workers +- [cookieconsent](https://github.com/osano/cookieconsent) thanks to EU laws ### Features / Contents diff --git a/Stats.js b/Stats.js deleted file mode 100644 index bcddea8..0000000 --- a/Stats.js +++ /dev/null @@ -1,147 +0,0 @@ -(function(global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global.Stats = factory()); -}(this, (function() { - 'use strict'; - - /** - * @author mrdoob / http://mrdoob.com/ - * @return {Object} stats - */ - /* eslint-disable require-jsdoc */ - const Stats = function() { - let mode = 0; - - const container = document.createElement('div'); - container.style.cssText = 'position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:90000'; - container.addEventListener('click', function(event) { - event.preventDefault(); - showPanel(++mode % container.children.length); - }, false); - - function addPanel(panel) { - container.appendChild(panel.dom); - return panel; - } - - function showPanel(id) { - for (let i = 0; i < container.children.length; i++) { - container.children[i].style.display = i === id ? 'block' : 'none'; - } - mode = id; - } - - function dispose() { - for (let i = 0; i < container.children.length; i++) { - container.removeChild(container.children[i]); - } - container.parentNode.removeChild(container); - } - - let beginTime = (performance || Date).now(); let prevTime = beginTime; let frames = 0; - - const fpsPanel = addPanel(new Stats.Panel('FPS', '#0ff', '#002')); - const msPanel = addPanel(new Stats.Panel('MS', '#0f0', '#020')); - let memPanel; - - if (self.performance && self.performance.memory) { - memPanel = addPanel(new Stats.Panel('MB', '#f08', '#201')); - } - - showPanel(0); - - return { - REVISION: 16, - - dom: container, - - addPanel: addPanel, - showPanel: showPanel, - dispose: dispose, - // Backwards Compatibility - domElement: container, - setMode: showPanel, - - begin: function() { - beginTime = (performance || Date).now(); - }, - end: function() { - frames++; - const time = (performance || Date).now(); - msPanel.update(time - beginTime, 200); - - if (time >= prevTime + 1000) { - fpsPanel.update((frames * 1000) / (time - prevTime), 100); - prevTime = time; - frames = 0; - - if (memPanel) { - const memory = performance.memory; - memPanel.update(memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576); - } - } - return time; - }, - update: function() { - beginTime = this.end(); - }, - }; - }; - - Stats.Panel = function(name, fg, bg) { - let min = Infinity; let max = 0; const round = Math.round; - const PR = round(window.devicePixelRatio || 1); - - const WIDTH = 80 * PR; const HEIGHT = 48 * PR; - const TEXT_X = 3 * PR; const TEXT_Y = 2 * PR; - const GRAPH_X = 3 * PR; const GRAPH_Y = 15 * PR; - const GRAPH_WIDTH = 74 * PR; const GRAPH_HEIGHT = 30 * PR; - - const canvas = document.createElement('canvas'); - canvas.width = WIDTH; - canvas.height = HEIGHT; - canvas.style.cssText = 'width:80px;height:48px'; - - const context = canvas.getContext('2d'); - context.font = 'bold ' + (9 * PR) + 'px Helvetica,Arial,sans-serif'; - context.textBaseline = 'top'; - - context.fillStyle = bg; - context.fillRect(0, 0, WIDTH, HEIGHT); - - context.fillStyle = fg; - context.fillText(name, TEXT_X, TEXT_Y); - context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT); - - context.fillStyle = bg; - context.globalAlpha = 0.9; - context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT); - - return { - - dom: canvas, - - update: function(value, maxValue) { - min = Math.min(min, value); - max = Math.max(max, value); - - context.fillStyle = bg; - context.globalAlpha = 1; - context.fillRect(0, 0, WIDTH, GRAPH_Y); - context.fillStyle = fg; - context.fillText(round(value) + ' ' + name + ' (' + round(min) + '-' + round(max) + ')', TEXT_X, TEXT_Y); - - context.drawImage(canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT); - - context.fillRect(GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT); - - context.fillStyle = bg; - context.globalAlpha = 0.9; - context.fillRect(GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round((1 - (value / maxValue)) * GRAPH_HEIGHT)); - }, - }; - }; - - return Stats; -}))); diff --git a/docs/BasePass.html b/docs/BasePass.html new file mode 100644 index 0000000..03341cd --- /dev/null +++ b/docs/BasePass.html @@ -0,0 +1,203 @@ + + + + + + + + BasePass + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Interface

    +

    BasePass

    +
    + + + + + +
    + +
    + +

    BasePass

    + + +
    + +
    +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + +
    Author:
    +
    +
      +
    • alteredq / http://alteredqualia.com/
    • + +
    • hexxone / https://hexx.one Basic shader pass interface
    • +
    +
    + + + + + + + + + + + + + + +

    + View Source + + three/pass/BasePass.ts, line 4 + +

    + +
    + + + + +
    + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/BaseShader.html b/docs/BaseShader.html new file mode 100644 index 0000000..0e5c712 --- /dev/null +++ b/docs/BaseShader.html @@ -0,0 +1,596 @@ + + + + + + + + BaseShader + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Interface

    +

    BaseShader

    +
    + + + + + +
    + +
    + +

    BaseShader

    + + +
    + +
    +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + +
    Author:
    +
    +
      +
    • hexxone / https://hexx.one
    • +
    +
    + + + + + +
    License:
    +
    • Copyright (c) 2021 hexxone All rights reserved. Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
    + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 4 + +

    + +
    + + + + +
    + + + + + + + + + + + + + + +
    +

    Members

    +
    + +
    + + + + +any + + + + +

    + # + + + defines + + +

    + + + + +
    + glsl defines +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 37 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + fragmentShader + + +

    + + + + +
    + glsl fragment shader coder +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 25 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + shaderID + + +

    + + + + +
    + short name description for the shader +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 13 + +

    + +
    + + + + + +
    + +
    + + + + +any + + + + +

    + # + + + uniforms + + +

    + + + + +
    + glsl shared uniforms +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 31 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + vertexShader + + +

    + + + + +
    + glsl vertex shader coder +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 19 + +

    + +
    + + + + + +
    + +
    +
    + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/BlendShader.html b/docs/BlendShader.html new file mode 100644 index 0000000..120fdab --- /dev/null +++ b/docs/BlendShader.html @@ -0,0 +1,692 @@ + + + + + + + + BlendShader + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    BlendShader

    +
    + + + + + +
    + +
    + +

    BlendShader()

    + +
    Blend another texture in and out
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new BlendShader() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BlendShader.ts, line 7 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    BlendShader
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + + + + +any + + + + +

    + # + + + defines + + +

    + + + + +
    + glsl defines +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 37 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + fragmentShader + + +

    + + + + +
    + glsl fragment shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 25 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + shaderID + + +

    + + + + +
    + short name description for the shader +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 13 + +

    + +
    + + + + + +
    + +
    + + + + +any + + + + +

    + # + + + uniforms + + +

    + + + + +
    + glsl shared uniforms +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 31 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + vertexShader + + +

    + + + + +
    + glsl vertex shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 19 + +

    + +
    + + + + + +
    + +
    +
    + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/BlurShader.html b/docs/BlurShader.html new file mode 100644 index 0000000..606b2f0 --- /dev/null +++ b/docs/BlurShader.html @@ -0,0 +1,692 @@ + + + + + + + + BlurShader + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    BlurShader

    +
    + + + + + +
    + +
    + +

    BlurShader()

    + +
    Blur shader with Alpha support
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new BlurShader() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BlurShader.ts, line 16 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    BlurShader
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + + + + +any + + + + +

    + # + + + defines + + +

    + + + + +
    + glsl defines +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 37 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + fragmentShader + + +

    + + + + +
    + glsl fragment shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 25 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + shaderID + + +

    + + + + +
    + short name description for the shader +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 13 + +

    + +
    + + + + + +
    + +
    + + + + +any + + + + +

    + # + + + uniforms + + +

    + + + + +
    + glsl shared uniforms +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 31 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + vertexShader + + +

    + + + + +
    + glsl vertex shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 19 + +

    + +
    + + + + + +
    + +
    +
    + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/CComponent.html b/docs/CComponent.html index 9ff1c7b..30f0d8e 100644 --- a/docs/CComponent.html +++ b/docs/CComponent.html @@ -66,7 +66,7 @@
    @@ -227,11 +227,11 @@

    Members

    -

    - # +

    + # - children + _internal_children

    @@ -282,7 +282,7 @@

    View Source - CComponent.ts, line 16 + CComponent.ts, line 12

    @@ -304,11 +304,11 @@

    -

    - # +

    + # - children + _internal_children

    @@ -359,7 +359,7 @@

    View Source - CComponent.ts, line 74 + CComponent.ts, line 72

    @@ -428,7 +428,7 @@

    View Source - CComponent.ts, line 12 + CComponent.ts, line 17

    @@ -505,7 +505,7 @@

    View Source - CComponent.ts, line 68 + CComponent.ts, line 79

    @@ -671,7 +671,7 @@

    Parameters:

    View Source - CComponent.ts, line 27 + CComponent.ts, line 29

    @@ -793,7 +793,7 @@

    View Source - CComponent.ts, line 41 + CComponent.ts, line 45

    @@ -890,7 +890,7 @@

    View Source - CComponent.ts, line 54 + CComponent.ts, line 59

    diff --git a/docs/CComponent.ts.html b/docs/CComponent.ts.html index aea6e01..5dfe845 100644 --- a/docs/CComponent.ts.html +++ b/docs/CComponent.ts.html @@ -68,7 +68,7 @@

    @@ -104,20 +104,22 @@

    CComponent.ts

    private needsUpdate = false; /** - * main Settings, need to be overwritten with Specific settings + * Important: Append your child objects, for settings to be applied correctly! */ - public settings: CSettings = null; + _internal_children: CComponent[] = []; /** - * Important: Append your child objects, for settings to be applied correctly! + * main Settings, need to be overwritten with Specific settings + * @public */ - public children: CComponent[] = []; + public settings: CSettings = null; /** * will recursively try to set a setting with type and return success * <br/> * will also flag the module as needs-Update. * + * @public * @param {Object} key * @param {Object} value * @return {boolean} found @@ -128,7 +130,7 @@

    CComponent.ts

    this.needsUpdate = true; Smallog.debug(`ApplySetting: ${key}:${value}`); } - this.children.forEach((ch) => found = (found || ch.applySetting(key, value))); + this._internal_children.forEach((ch) => found = ch.applySetting(key, value) || found); return found; } @@ -136,9 +138,11 @@

    CComponent.ts

    * DO NOT OVERWWRITE !!! * <br/> * will recursively update all needed modules after settings changes + * + * @public */ - public updateAll() { - this.children.forEach((c) => c.updateAll()); + public updateAll(): void { + this._internal_children.forEach((c) => c.updateAll()); if (this.needsUpdate) this.updateSettings(); this.needsUpdate = false; } @@ -148,6 +152,7 @@

    CComponent.ts

    * <br/> * should usually get called automatically when needed.. no need for extra calling * + * @public * @return {Promise} async commpletion event */ public updateSettings(): Promise<void> { diff --git a/docs/CSettings.html b/docs/CSettings.html index 8f9c0cb..a384357 100644 --- a/docs/CSettings.html +++ b/docs/CSettings.html @@ -66,7 +66,7 @@
    @@ -174,7 +174,7 @@

    View Source - CSettings.ts, line 20 + CSettings.ts, line 21

    @@ -245,7 +245,7 @@

    - check if a certain key exists on a (dereived) object and the value type matches + check if a certain key exists on a (dereived) object. if it exists, the value type matches and the value is not already equal, apply & return true otherwise return false
    @@ -372,7 +372,7 @@

    Parameters:

    View Source - CSettings.ts, line 27 + CSettings.ts, line 32

    @@ -403,7 +403,7 @@
    Parameters:
    -
    success
    +
    value was found & changed
    @@ -103,20 +103,27 @@

    CSettings.ts

    * * All Settings-classes should be dereived from this one. * +* @public * @see {CComponent} */ export class CSettings { /** - * check if a certain key exists on a (dereived) object and the value type matches + * check if a certain key exists on a (dereived) object. + * if it exists, the value type matches and the value is not already equal, apply & return true + * otherwise return false + * + * @public * @param {string} key * @param {Object} castedValue - * @return {boolean} success + * @return {boolean} value was found & changed */ - public apply(key: string, castedValue: any) { + public apply(key: string, castedValue: any): boolean { if (this[key] !== undefined) { if (typeof this[key] === typeof castedValue) { - this[key] = castedValue; - return true; + if (this[key] !== castedValue) { + this[key] = castedValue; + return true; + } } else { Smallog.error('CSettings Error: invalid type on: \'' + key + '\'. Is: \'' + typeof this[key] + '\', applied: \'' + typeof castedValue + '\''); diff --git a/docs/CUESettings.html b/docs/CUESettings.html index 70ec6bc..d3c70e5 100644 --- a/docs/CUESettings.html +++ b/docs/CUESettings.html @@ -1,479 +1,479 @@ - - - - - - - - CUESettings - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    CUESettings

    -
    - - - - - -
    - -
    - -

    CUESettings()

    - -
    iCUE processing settings
    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new CUESettings() - - -

    - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEICUE.ts, line 23 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - -

    Extends

    - - - - - - - - - - - - -

    Classes

    - -
    -
    CUESettings
    -
    -
    - - - - - - - - - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - apply(key, castedValue) → {boolean} - - -

    - - - - -
    - check if a certain key exists on a (dereived) object and the value type matches -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    key - - -string - - - -
    castedValue - - -Object - - - -
    -
    - - - - - -
    - - - - - - - - -
    Overrides:
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CSettings.ts, line 27 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    success
    - - -
    - - -boolean - - -
    - -
    - - -
    -
    - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - + + + + + + + + CUESettings + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    CUESettings

    +
    + + + + + +
    + +
    + +

    CUESettings()

    + +
    iCUE processing settings
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new CUESettings() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEICUE.ts, line 25 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + +

    Classes

    + +
    +
    CUESettings
    +
    +
    + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + apply(key, castedValue) → {boolean} + + +

    + + + + +
    + check if a certain key exists on a (dereived) object. if it exists, the value type matches and the value is not already equal, apply & return true otherwise return false +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +string + + + +
    castedValue + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CSettings.ts, line 32 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    value was found & changed
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/docs/ChromaticShader.html b/docs/ChromaticShader.html new file mode 100644 index 0000000..466b926 --- /dev/null +++ b/docs/ChromaticShader.html @@ -0,0 +1,692 @@ + + + + + + + + ChromaticShader + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    ChromaticShader

    +
    + + + + + +
    + +
    + +

    ChromaticShader()

    + +
    Chromatic Abberation shader with alpha support
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new ChromaticShader() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/ChromaticShader.ts, line 16 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    ChromaticShader
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + + + + +any + + + + +

    + # + + + defines + + +

    + + + + +
    + glsl defines +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 37 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + fragmentShader + + +

    + + + + +
    + glsl fragment shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 25 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + shaderID + + +

    + + + + +
    + short name description for the shader +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 13 + +

    + +
    + + + + + +
    + +
    + + + + +any + + + + +

    + # + + + uniforms + + +

    + + + + +
    + glsl shared uniforms +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 31 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + vertexShader + + +

    + + + + +
    + glsl vertex shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 19 + +

    + +
    + + + + + +
    + +
    +
    + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/CopyShader.html b/docs/CopyShader.html new file mode 100644 index 0000000..c7c4bd6 --- /dev/null +++ b/docs/CopyShader.html @@ -0,0 +1,692 @@ + + + + + + + + CopyShader + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    CopyShader

    +
    + + + + + +
    + +
    + +

    CopyShader()

    + +
    Siimple I/O shader
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new CopyShader() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/CopyShader.ts, line 7 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    CopyShader
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + + + + +any + + + + +

    + # + + + defines + + +

    + + + + +
    + glsl defines +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 37 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + fragmentShader + + +

    + + + + +
    + glsl fragment shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 25 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + shaderID + + +

    + + + + +
    + short name description for the shader +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 13 + +

    + +
    + + + + + +
    + +
    + + + + +any + + + + +

    + # + + + uniforms + + +

    + + + + +
    + glsl shared uniforms +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 31 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + vertexShader + + +

    + + + + +
    + glsl vertex shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 19 + +

    + +
    + + + + + +
    + +
    +
    + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/EffectComposer.html b/docs/EffectComposer.html new file mode 100644 index 0000000..10c2f55 --- /dev/null +++ b/docs/EffectComposer.html @@ -0,0 +1,1430 @@ + + + + + + + + EffectComposer + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    EffectComposer

    +
    + + + + + +
    + +
    + +

    EffectComposer(scene, camera, renderer, globalPrec, renderTarget)

    + +
    render shader chain
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new EffectComposer(scene, camera, renderer, globalPrec, renderTarget) + + +

    + + + + +
    + Instantiate +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    scene + + +Scene + + + + + +
    camera + + +PerspectiveCamera + + + + + +
    renderer + + +WebGLRenderer + + + + + +
    globalPrec + + +string + + + + + + mediump + +
    renderTarget + + +WebGLRenderTarget + + + + + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/EffectComposer.ts, line 20 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    EffectComposer
    +
    +
    + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + addPass(pass) + + +

    + + + + +
    + Append a shader to the chain +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    pass + + +BasePass + + + + Shader to add
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/EffectComposer.ts, line 65 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + insertPass(pass, index) + + +

    + + + + +
    + Insert a shader in the chain +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    pass + + +BasePass + + + + Shader to add
    index + + +number + + + + position
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/EffectComposer.ts, line 76 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + isLastEnabledPass(passIndex) → {boolean} + + +

    + + + + +
    + Checks if the given shader should be rendererd to screen +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    passIndex + + +number + + + + position
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/EffectComposer.ts, line 86 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + render(deltaTime, frame) + + +

    + + + + +
    + Render the shader-chain for 1 frame +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    deltaTime + + +number + + + + if not given, will calculate its own
    frame + + +XRFrame + + + + Currently rendering XR frame?
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/EffectComposer.ts, line 99 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + reset(renderTarget) + + +

    + + + + +
    + Destroy the current shader-chain +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    renderTarget + + +WebGLRenderTarget + + + + target to Reset (optional)
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/EffectComposer.ts, line 182 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + setSize(width, height) + + +

    + + + + +
    + Updated buffer size +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    width + + +number + + + + X
    height + + +number + + + + Y
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/EffectComposer.ts, line 203 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/FPSettings.html b/docs/FPSettings.html new file mode 100644 index 0000000..cce98e7 --- /dev/null +++ b/docs/FPSettings.html @@ -0,0 +1,479 @@ + + + + + + + + FPSettings + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    FPSettings

    +
    + + + + + +
    + +
    + +

    FPSettings()

    + +
    Custom Stats settings
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new FPSettings() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + FPSta.ts, line 20 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + +

    Classes

    + +
    +
    FPSettings
    +
    +
    + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + apply(key, castedValue) → {boolean} + + +

    + + + + +
    + check if a certain key exists on a (dereived) object. if it exists, the value type matches and the value is not already equal, apply & return true otherwise return false +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +string + + + +
    castedValue + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CSettings.ts, line 32 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    value was found & changed
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/FPSta.ts.html b/docs/FPSta.ts.html new file mode 100644 index 0000000..19ddecf --- /dev/null +++ b/docs/FPSta.ts.html @@ -0,0 +1,326 @@ + + + + + + + + + + FPSta.ts + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Source

    +

    FPSta.ts

    +
    + + + + + +
    +
    +
    /**
    +* @author hexxone / https://hexx.one
    +*
    +* @license
    +* Copyright (c) 2021 hexxone All rights reserved.
    +* Licensed under the GNU GENERAL PUBLIC LICENSE.
    +* See LICENSE file in the project root for full license information.
    +*/
    +
    +import {CComponent} from './CComponent';
    +import {CSettings} from './CSettings';
    +import {Smallog} from './Smallog';
    +import {waitReady} from './Util';
    +import {WEAS} from './WEAS';
    +
    +const ELM_ID = 'fpstats';
    +
    +/**
    +* Custom Stats settings
    +* @public
    +* @extends {CSettings}
    +*/
    +export class FPSettings extends CSettings {
    +	debugging : boolean = false;
    +}
    +
    +/**
    +* Custom FPS Stats module
    +* @public
    +* @extends {CComponent}
    +*/
    +export class FPStats extends CComponent {
    +	public settings: FPSettings = new FPSettings();
    +
    +	private container: HTMLElement;
    +	// FPS
    +	private fpsHolder: HTMLElement;
    +	private lastUpdate: number = performance.now();
    +	private frameCount: number = 0;
    +	// usage
    +	private useHolder: HTMLElement;
    +	// cpu
    +	private cpuHolder: HTMLElement;
    +	private cpuBegin: number = performance.now();
    +	private cpuEnd: number = performance.now();
    +	private cpuMS: number = 0;
    +	// gpu
    +	private gpuHolder: HTMLElement;
    +	private gpuBegin: number = performance.now();
    +	private gpuEnd: number = performance.now();
    +	private gpuMS: number = 0;
    +	// audio
    +	private auProvider: WEAS = null;
    +	private audHolder: HTMLElement;
    +	private audioMS: number = 0;
    +
    +
    +	/**
    +	* Create hidden element
    +	* @param {WEAS} audio (optional)
    +	* @param {WEICUE} cue (optional)
    +	*/
    +	constructor(audio?: WEAS) {
    +		super();
    +		this.auProvider = audio;
    +
    +		waitReady().then(() => {
    +			this.injectCSS();
    +			this.injectHTML();
    +		});
    +	}
    +
    +	/**
    +	* Make style
    +	* @ignore
    +	*/
    +	private injectCSS() {
    +		const st = document.createElement('style');
    +		st.innerHTML = `
    +		#${ELM_ID} {
    +			opacity: 0;
    +			position: fixed;
    +			top: 50vh;
    +			left: 10px;
    +			padding: 10px;
    +			z-index: 90000;
    +			font-size: 1.5em;
    +			text-align: left;
    +			background: black;
    +		}
    +		#${ELM_ID}.show {
    +			opacity: 0.8;
    +		}
    +		`;
    +		document.head.append(st);
    +	}
    +
    +	/**
    +	* Make dom
    +	* @ignore
    +	*/
    +	private injectHTML() {
    +		// root
    +		this.container = document.createElement('div');
    +		this.container.id = ELM_ID;
    +		document.body.append(this.container);
    +		// fps
    +		this.fpsHolder = document.createElement('div');
    +		this.fpsHolder.innerText = 'FPS: 0';
    +		// usage
    +		this.useHolder = document.createElement('div');
    +		this.useHolder.innerText = 'Usage: 0 %';
    +		// cpu
    +		this.cpuHolder = document.createElement('div');
    +		this.cpuHolder.innerText = 'CPU: 0 ms';
    +		// gpu
    +		this.gpuHolder = document.createElement('div');
    +		this.gpuHolder.innerText = 'GPU: 0 ms';
    +		// append
    +		this.container.append(this.fpsHolder, this.useHolder, this.cpuHolder, this.gpuHolder);
    +		// audio
    +		if (this.auProvider) {
    +			this.audHolder = document.createElement('div');
    +			this.audHolder.innerText = 'Audio: 0 ms';
    +			this.container.append(this.audHolder);
    +		}
    +	}
    +
    +	/**
    +	* update visible
    +	* @public
    +	* @return {Promise}
    +	*/
    +	public updateSettings(): Promise<void> {
    +		// show or hide debug info
    +		return waitReady().then(() => {
    +			if (this.settings.debugging) this.container.classList.add('show');
    +			else this.container.classList.remove('show');
    +		});
    +	}
    +
    +	/**
    +	* Start measuring interval
    +	* @public
    +	* @param {boolean} cpu True if Cpu, false if GPU
    +	*/
    +	public begin(cpu: boolean) {
    +		if (!this.settings.debugging) return;
    +
    +		if (cpu) this.cpuBegin = performance.now();
    +		else this.gpuBegin = performance.now();
    +	}
    +
    +	/**
    +	* End measuring interval
    +	* @public
    +	* @param {boolean} cpu True if Cpu, false if GPU
    +	*/
    +	public end(cpu: boolean) {
    +		if (!this.settings.debugging) return;
    +
    +		if (cpu) this.cpuEnd = performance.now();
    +		else this.gpuEnd = performance.now();
    +	}
    +
    +	/**
    +	* update the html representation
    +	* @public
    +	*/
    +	public update() {
    +		this.frameCount++;
    +		this.cpuMS += (this.cpuEnd - this.cpuBegin);
    +		this.gpuMS += (this.gpuEnd - this.gpuBegin);
    +
    +		if (this.auProvider && this.auProvider.lastAudio) {
    +			this.audioMS = (this.audioMS + this.auProvider.lastAudio.ellapsed) / 2;
    +		}
    +
    +		// only update text ~every second
    +		const now = performance.now();
    +		if (now < (this.lastUpdate + 1000)) return;
    +
    +		// calculate
    +		const elapsd = (now - this.lastUpdate) / 1000;
    +		const efpies = this.frameCount / elapsd;
    +		const yusage = (this.cpuMS + this.gpuMS) / 900;
    +		const cepeyu = this.cpuMS / this.frameCount;
    +		const gepeyu = this.gpuMS / this.frameCount;
    +
    +		// apply
    +		const da = this.fpsHolder.innerText = `FPS: ${efpies.toFixed(2)}`;
    +		const db = this.useHolder.innerText = `Usage: ${yusage.toFixed(2)} %`;
    +		const dc = this.cpuHolder.innerText = `CPU: ${cepeyu.toFixed(2)} ms`;
    +		const dd = this.gpuHolder.innerText = `GPU: ${gepeyu.toFixed(2)} ms`;
    +		Smallog.debug(`${da} ${db} ${dc} ${dd}`, '[FPStats]');
    +
    +		if (this.audHolder) this.audHolder.innerText = `Audio: ${this.audioMS.toFixed(2)} ms`;
    +
    +		this.lastUpdate = now;
    +		this.reset();
    +	}
    +
    +	/**
    +	* All back to 0
    +	* @public
    +	*/
    +	public reset() {
    +		this.frameCount = this.cpuMS = this.gpuMS = this.audioMS = 0;
    +	}
    +}
    +
    +
    +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + diff --git a/docs/FPStats.html b/docs/FPStats.html new file mode 100644 index 0000000..afff2e6 --- /dev/null +++ b/docs/FPStats.html @@ -0,0 +1,1431 @@ + + + + + + + + FPStats + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    FPStats

    +
    + + + + + +
    + +
    + +

    FPStats(audio, cue)

    + +
    Custom FPS Stats module
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new FPStats(audio, cue) + + +

    + + + + +
    + Create hidden element +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    audio + + +WEAS + + + + (optional)
    cue + + +WEICUE + + + + (optional)
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + FPSta.ts, line 31 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + +

    Classes

    + +
    +
    FPStats
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + +

    + # + + + _internal_children + + +

    + + + + +
    + Important: Append your child objects, for settings to be applied correctly! +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 12 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + settings + + +

    + + + + +
    + main Settings, need to be overwritten with Specific settings +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 17 + +

    + +
    + + + + + +
    + +
    +
    + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + applySetting(key, value) → {boolean} + + +

    + + + + +
    + will recursively try to set a setting with type and return success
    will also flag the module as needs-Update. +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +Object + + + +
    value + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 29 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    found
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + begin(cpu) + + +

    + + + + +
    + Start measuring interval +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    cpu + + +boolean + + + + True if Cpu, false if GPU
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + FPSta.ts, line 130 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + end(cpu) + + +

    + + + + +
    + End measuring interval +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    cpu + + +boolean + + + + True if Cpu, false if GPU
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + FPSta.ts, line 143 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + reset() + + +

    + + + + +
    + All back to 0 +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + FPSta.ts, line 187 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + update() + + +

    + + + + +
    + update the html representation +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + FPSta.ts, line 155 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + updateAll() + + +

    + + + + +
    + DO NOT OVERWWRITE !!!
    will recursively update all needed modules after settings changes +
    + + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 45 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + updateSettings() → {Promise} + + +

    + + + + +
    + update visible +
    + + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + FPSta.ts, line 116 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + + +
    + + +Promise + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/FXAAShader.html b/docs/FXAAShader.html new file mode 100644 index 0000000..383fef7 --- /dev/null +++ b/docs/FXAAShader.html @@ -0,0 +1,692 @@ + + + + + + + + FXAAShader + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    FXAAShader

    +
    + + + + + +
    + +
    + +

    FXAAShader()

    + +
    NVIDIA FXAA by Timothy Lottes http://timothylottes.blogspot.com/2011/06/fxaa3-source-released.html - WebGL port by @supereggbert http://www.glge.org/demos/fxaa/
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new FXAAShader() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/FXAAShader.ts, line 17 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    FXAAShader
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + + + + +any + + + + +

    + # + + + defines + + +

    + + + + +
    + glsl defines +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 37 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + fragmentShader + + +

    + + + + +
    + glsl fragment shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 25 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + shaderID + + +

    + + + + +
    + short name description for the shader +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 13 + +

    + +
    + + + + + +
    + +
    + + + + +any + + + + +

    + # + + + uniforms + + +

    + + + + +
    + glsl shared uniforms +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 31 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + vertexShader + + +

    + + + + +
    + glsl vertex shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 19 + +

    + +
    + + + + + +
    + +
    +
    + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/FractalMirrorShader.html b/docs/FractalMirrorShader.html new file mode 100644 index 0000000..b73e251 --- /dev/null +++ b/docs/FractalMirrorShader.html @@ -0,0 +1,692 @@ + + + + + + + + FractalMirrorShader + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    FractalMirrorShader

    +
    + + + + + +
    + +
    + +

    FractalMirrorShader()

    + +
    Customized Kaleidoscope shader Inspired by ackleyrc: https://www.shadertoy.com/view/llXcRl
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new FractalMirrorShader() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/FractalMirrorShader.ts, line 20 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    FractalMirrorShader
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + + + + +any + + + + +

    + # + + + defines + + +

    + + + + +
    + glsl defines +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 37 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + fragmentShader + + +

    + + + + +
    + glsl fragment shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 25 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + shaderID + + +

    + + + + +
    + short name description for the shader +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 13 + +

    + +
    + + + + + +
    + +
    + + + + +any + + + + +

    + # + + + uniforms + + +

    + + + + +
    + glsl shared uniforms +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 31 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + vertexShader + + +

    + + + + +
    + glsl vertex shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 19 + +

    + +
    + + + + + +
    + +
    +
    + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/FullScreenHelper.html b/docs/FullScreenHelper.html new file mode 100644 index 0000000..8526097 --- /dev/null +++ b/docs/FullScreenHelper.html @@ -0,0 +1,713 @@ + + + + + + + + FullScreenHelper + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    FullScreenHelper

    +
    + + + + + +
    + +
    + +

    FullScreenHelper(material)

    + +
    Helper for passes that need to fill the viewport with a single quad. used to render on a PlaneGeometry ("texture")
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new FullScreenHelper(material) + + +

    + + + + +
    + instantiate +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    material + + +Material + + + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/FullScreenHelper.ts, line 13 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    FullScreenHelper
    +
    +
    + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + dispose() + + +

    + + + + +
    + Destroy 2D-environment +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/FullScreenHelper.ts, line 45 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + render(renderer) + + +

    + + + + +
    + Render the 2D-environment +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    renderer + + +WebGLRenderer + + + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/FullScreenHelper.ts, line 39 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + setMaterial(mat) + + +

    + + + + +
    + Change mesh material +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    mat + + +Material + + + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/FullScreenHelper.ts, line 32 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/LUTShader.html b/docs/LUTShader.html new file mode 100644 index 0000000..27cd96d --- /dev/null +++ b/docs/LUTShader.html @@ -0,0 +1,692 @@ + + + + + + + + LUTShader + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    LUTShader

    +
    + + + + + +
    + +
    + +

    LUTShader()

    + +
    LookUpTable shader taken from ThreeJS examples and converted to TS
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new LUTShader() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/LUTShader.ts, line 9 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    LUTShader
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + + + + +any + + + + +

    + # + + + defines + + +

    + + + + +
    + glsl defines +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 37 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + fragmentShader + + +

    + + + + +
    + glsl fragment shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 25 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + shaderID + + +

    + + + + +
    + short name description for the shader +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 13 + +

    + +
    + + + + + +
    + +
    + + + + +any + + + + +

    + # + + + uniforms + + +

    + + + + +
    + glsl shared uniforms +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 31 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + vertexShader + + +

    + + + + +
    + glsl vertex shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 19 + +

    + +
    + + + + + +
    + +
    +
    + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/LUTShaderNearest.html b/docs/LUTShaderNearest.html new file mode 100644 index 0000000..7e926b9 --- /dev/null +++ b/docs/LUTShaderNearest.html @@ -0,0 +1,721 @@ + + + + + + + + LUTShaderNearest + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    LUTShaderNearest

    +
    + + + + + +
    + +
    + +

    LUTShaderNearest()

    + +
    LookUpTable shader without filtering
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new LUTShaderNearest() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/LUTShaderNearest.ts, line 17 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + +

    Classes

    + +
    +
    LUTShaderNearest
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + + + + +any + + + + +

    + # + + + defines + + +

    + + + + +
    + glsl defines +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 37 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + fragmentShader + + +

    + + + + +
    + glsl fragment shader coder +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 25 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + shaderID + + +

    + + + + +
    + short name description for the shader +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 13 + +

    + +
    + + + + + +
    + +
    + + + + +any + + + + +

    + # + + + uniforms + + +

    + + + + +
    + glsl shared uniforms +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 31 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + vertexShader + + +

    + + + + +
    + glsl vertex shader coder +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 19 + +

    + +
    + + + + + +
    + +
    +
    + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/LuminosityHighPassShader.html b/docs/LuminosityHighPassShader.html new file mode 100644 index 0000000..f985502 --- /dev/null +++ b/docs/LuminosityHighPassShader.html @@ -0,0 +1,692 @@ + + + + + + + + LuminosityHighPassShader + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    LuminosityHighPassShader

    +
    + + + + + +
    + +
    + +

    LuminosityHighPassShader()

    + +
    Luminosity http://en.wikipedia.org/wiki/Luminosity
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new LuminosityHighPassShader() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/LuminosityHighPassShader.ts, line 19 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    LuminosityHighPassShader
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + + + + +any + + + + +

    + # + + + defines + + +

    + + + + +
    + glsl defines +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 37 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + fragmentShader + + +

    + + + + +
    + glsl fragment shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 25 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + shaderID + + +

    + + + + +
    + short name description for the shader +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 13 + +

    + +
    + + + + + +
    + +
    + + + + +any + + + + +

    + # + + + uniforms + + +

    + + + + +
    + glsl shared uniforms +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 31 + +

    + +
    + + + + + +
    + +
    + + + + +string + + + + +

    + # + + + vertexShader + + +

    + + + + +
    + glsl vertex shader coder +
    + + + + + +
    + + + + + + + + + + + + +
    Implements:
    +
    + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/shader/BaseShader.ts, line 19 + +

    + +
    + + + + + +
    + +
    +
    + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/OfflinePlugin.html b/docs/OfflinePlugin.html index d11118f..3e673e5 100644 --- a/docs/OfflinePlugin.html +++ b/docs/OfflinePlugin.html @@ -66,7 +66,7 @@
    diff --git a/docs/ReloadHelper.html b/docs/ReloadHelper.html index 85330de..fe9ada2 100644 --- a/docs/ReloadHelper.html +++ b/docs/ReloadHelper.html @@ -66,7 +66,7 @@
    @@ -225,6 +225,151 @@

    Classes

    +
    +

    Members

    +
    + +
    + +

    + # + + + settings + + +

    + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + ReloadHelper.ts, line 38 + +

    + +
    + + + + + +
    + +
    + + + + +ReloadSettings + + + + +

    + # + + + settings + + +

    + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + ReloadHelper.ts, line 145 + +

    + +
    + + + + + +
    + +
    +
    + @@ -103,21 +103,24 @@

    ReloadHelper.ts

    import {waitReady} from './Util'; /** - * Reload-bar settings - */ +* Reload-bar settings +*/ class ReloadSettings extends CSettings { reload_seconds: number = 3; } /** - * Visual Reload-Bar - */ +* Visual Reload-Bar +*/ export class ReloadHelper extends CComponent { + /** + * @public + */ public settings: ReloadSettings = new ReloadSettings(); /** - * Create and prepare when document is ready - */ + * Create and prepare when document is ready + */ constructor() { super(); waitReady().then(() => { @@ -127,9 +130,9 @@

    ReloadHelper.ts

    } /** - * Make custom style - * @ignore - */ + * Make custom style + * @ignore + */ private injectCSS() { const st = document.createElement('style'); st.innerHTML = ` @@ -176,9 +179,9 @@

    ReloadHelper.ts

    } /** - * Make custom html elements - * @ignore - */ + * Make custom html elements + * @ignore + */ private injectHTML() { const outer = document.createElement('div'); outer.id = 'reloadhelper'; @@ -192,9 +195,10 @@

    ReloadHelper.ts

    } /** - * @Todo test: bar always reset to 0 on show ?? - * @param {boolean} visible - */ + * @Todo test: bar always reset to 0 on show ?? + * @public + * @param {boolean} visible + */ public show(visible: boolean) { const e1 = document.getElementById('reload-bar'); const e2 = document.getElementById('reload-text'); @@ -208,9 +212,10 @@

    ReloadHelper.ts

    } /** - * dont print IMPL error - * @return {Promise} - */ + * dont print IMPL error + * @public + * @return {Promise} + */ public updateSettings(): Promise<void> { return Promise.resolve(); } diff --git a/docs/ReloadSettings.html b/docs/ReloadSettings.html index 0b801ee..633ccd7 100644 --- a/docs/ReloadSettings.html +++ b/docs/ReloadSettings.html @@ -66,7 +66,7 @@
    diff --git a/docs/RenderPass.html b/docs/RenderPass.html new file mode 100644 index 0000000..f98b54d --- /dev/null +++ b/docs/RenderPass.html @@ -0,0 +1,963 @@ + + + + + + + + RenderPass + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    RenderPass

    +
    + + + + + +
    + +
    + +

    RenderPass(scene, camera, overMat, clearColor, clearAlpha)

    + +
    Shader Render Helper
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new RenderPass(scene, camera, overMat, clearColor, clearAlpha) + + +

    + + + + +
    + Construct helper +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    scene + + +Scene + + + +
    camera + + +Camera + + + +
    overMat + + +Material + + + +
    clearColor + + +Color + + + +
    clearAlpha + + +number + + + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/RenderPass.ts, line 6 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    RenderPass
    +
    +
    + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + dispose() + + +

    + + + + +
    + Destroy shader +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/RenderPass.ts, line 35 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + render(renderer, writeBuffer, readBuffer, maskActive, renderToScreen, camera) + + +

    + + + + +
    + Render Frame +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    renderer + + +WebGLRenderer + + + + Context
    writeBuffer + + +WebGLRenderTarget + + + + Output
    readBuffer + + +WebGLRenderTarget + + + + Input
    maskActive + + +boolean + + + + filter
    renderToScreen + + +boolean + + + + render to canvas OR buffer
    camera + + +Camera + + + + (optional)
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/RenderPass.ts, line 54 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + setSize(width, height) + + +

    + + + + +
    + Updated screen size +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    width + + +number + + + + X
    height + + +number + + + + Y
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/RenderPass.ts, line 43 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/ShaderPass.html b/docs/ShaderPass.html new file mode 100644 index 0000000..fdf7b1d --- /dev/null +++ b/docs/ShaderPass.html @@ -0,0 +1,878 @@ + + + + + + + + ShaderPass + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    ShaderPass

    +
    + + + + + +
    + +
    + +

    ShaderPass(shader, textureID)

    + +
    ThreeJS Pass for easy full screen shaders
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new ShaderPass(shader, textureID) + + +

    + + + + +
    + Make Pass default Material will enable transparency! +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    shader + + +BaseShader +| + +ShaderMaterial + + + + + + Create From
    textureID + + +string + + + + + + tDiffuse + + Input Uniform Texture name
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/ShaderPass.ts, line 13 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    ShaderPass
    +
    +
    + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + dispose() + + +

    + + + + +
    + Destroy Pass +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/ShaderPass.ts, line 47 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + render(renderer, writeBuffer, readBuffer, maskActive, renderToScreen) + + +

    + + + + +
    + Render frame with chaining-support +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    renderer + + +WebGLRenderer + + + +
    writeBuffer + + +WebGLRenderTarget + + + + wB
    readBuffer + + +WebGLRenderTarget + + + + rB
    maskActive + + +boolean + + + + mA
    renderToScreen + + +boolean + + + + render to canvas OR buffer
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/ShaderPass.ts, line 68 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + setSize(width, height) + + +

    + + + + +
    + Canvas size update +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    width + + +number + + + + X
    height + + +number + + + + Y
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/ShaderPass.ts, line 56 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/Smallog.ts.html b/docs/Smallog.ts.html index 7d5784c..5937c3a 100644 --- a/docs/Smallog.ts.html +++ b/docs/Smallog.ts.html @@ -68,7 +68,7 @@ @@ -98,12 +98,12 @@

    Smallog.ts

    /* eslint-disable no-unused-vars */ /** - * trace exception calls - * @param {string} def error message - * @param {number} depth which call to pick - * @return {string} - * @ignore - */ +* trace exception calls +* @param {string} def error message +* @param {number} depth which call to pick +* @return {string} +* @ignore +*/ function traceCall(def: string, depth: number = 3) { try { throw new Error('TraceCall()'); @@ -111,110 +111,99 @@

    Smallog.ts

    // Examine e.stack here if (e.stack) { const splt = e.stack.split(/\n/); - if (splt.length > depth) return '[' + splt[depth].trim().substring(3) + '] '; + let trim = splt[depth].trim(); + if (trim.indexOf('at ') == 0) trim = trim.substring(3); + if (splt.length > depth) return '[' + trim + '] '; } } return def; } /** - * @see {Smallog} - */ +* @see {Smallog} +* @public +*/ export enum LogLevel { /** - * Print only error messages - */ + * Print only error messages + */ Error = 0, /** - * Print error and info messages - */ + * Print error and info messages + */ Info = 1, /** - * Print all messages - */ + * Print all messages + */ Debug = 2 } /** - * Small logging util, with name/time prefix & log levels - */ +* Small logging util, with name/time prefix & log levels +* @public +*/ export module Smallog { - - let logLevel: LogLevel = LogLevel.Debug; // todo level Info for release + const logLevel: LogLevel = 2; let preFix: string = '[Smallog] '; let printTime: boolean = false; /** - * get logging output level - * @return {LogLevel} current - */ + * get logging output level + * @return {LogLevel} current + */ export function getLevel() { return logLevel; } /** - * set logging output level - * @param {LogLevel} level new - */ - export function setLevel(level: LogLevel) { - logLevel = level; - } - - /** - * set logging prefix - * @param {string} pre - */ + * set logging prefix + * @param {string} pre + */ export function setPrefix(pre: string) { preFix = pre; } /** - * set time prefix - * @param {boolean} print - */ + * set time prefix + * @param {boolean} print + */ export function setPrintTime(print: boolean) { printTime = print; } /** - * print error message - * @param {string} msg log - * @param {string} hdr overwrite header - */ + * print error message + * @param {string} msg log + * @param {string} hdr overwrite header + */ export function error(msg: string, hdr: string = preFix) { - log(console.error, msg, traceCall(hdr)); + if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + console.error(hdr + msg); } /** - * print info message - * @param {string} msg log - * @param {string} hdr overwrite header - */ + * print info message + * @param {string} msg log + * @param {string} hdr overwrite header + */ export function info(msg: string, hdr: string = preFix) { - if (logLevel >= 2) hdr = traceCall(hdr); - if (logLevel >= 1) log(console.info, msg, hdr); + if (logLevel >= 1) { + if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + if (logLevel >= 2) hdr = traceCall(hdr); + console.info(hdr + msg); + } } /** - * print debug message - * @param {string} msg log - * @param {string} hdr overwrite header - */ + * print debug message + * @param {string} msg log + * @param {string} hdr overwrite header + */ export function debug(msg: string, hdr: string = preFix) { - if (logLevel >= 2) log(console.debug, msg, traceCall(hdr)); - } - - /** - * internal logging function - * @param {Function} call log callback - * @param {string} msg text to print - * @param {string} hdr header to include - * @ignore - */ - function log(call: (...data: any) => void, msg: string, hdr: string) { - let m = msg; - if (printTime) m = ('[' + new Date().toLocaleString() + '] ') + m; - call(hdr + m); + if (logLevel >= 2) { + if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + console.debug(hdr + msg); + } } }
    diff --git a/docs/UnrealBloomPass.html b/docs/UnrealBloomPass.html new file mode 100644 index 0000000..eef134f --- /dev/null +++ b/docs/UnrealBloomPass.html @@ -0,0 +1,1255 @@ + + + + + + + + UnrealBloomPass + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    UnrealBloomPass

    +
    + + + + + +
    + +
    + +

    UnrealBloomPass(resolution, strength, radius, threshold)

    + +
    Inspired from Unreal Engine https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new UnrealBloomPass(resolution, strength, radius, threshold) + + +

    + + + + +
    + Construct bloom shader +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    resolution + + +Vector2 + + + + size
    strength + + +number + + + + multiplier
    radius + + +number + + + + size
    threshold + + +number + + + + min val
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/UnrealBloomPass.ts, line 15 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + + + + + +

    Classes

    + +
    +
    UnrealBloomPass
    +
    +
    + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + dispose() + + +

    + + + + +
    + Destroy shader +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/UnrealBloomPass.ts, line 126 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + getCompositeMaterial(nMips) → {ShaderMaterial} + + +

    + + + + +
    + Make helper material +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    nMips + + +number + + + + MipMaps
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/UnrealBloomPass.ts, line 277 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + + +
    + + +ShaderMaterial + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + getSeperableBlurMaterial(kernelRadius) → {ShaderMaterial} + + +

    + + + + +
    + Make seperable material +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    kernelRadius + + +number + + + + size
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/UnrealBloomPass.ts, line 225 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + + +
    + + +ShaderMaterial + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + render(renderer, writeBuffer, readBuffer, maskActive, renderToScreen) + + +

    + + + + +
    + Render Frame +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    renderer + + +WebGLRenderer + + + + Context
    writeBuffer + + +WebGLRenderTarget + + + + Output
    readBuffer + + +WebGLRenderTarget + + + + Input
    maskActive + + +boolean + + + + filter
    renderToScreen + + +boolean + + + + render to canvas OR buffer
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/UnrealBloomPass.ts, line 162 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + setSize(width, height) + + +

    + + + + +
    + Updated screen size +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    width + + +number + + + + X
    height + + +number + + + + Y
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/pass/UnrealBloomPass.ts, line 141 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/Util.ts.html b/docs/Util.ts.html index ea24760..1084d76 100644 --- a/docs/Util.ts.html +++ b/docs/Util.ts.html @@ -68,7 +68,7 @@ @@ -93,12 +93,22 @@

    Util.ts

    * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * -* @description -* Shorthand Document ready wrapper +* @ignore */ +// promise resolve queue +const promQueue: ((val) => void)[] = []; +const workQueue = () => { + while (promQueue.length > 0) { + const call = promQueue.shift(); + call(true); + } +}; +document.addEventListener('DOMContentLoaded', workQueue, false); + /** -* Helper function, resolves when html document is ready +* Shorthand Document ready wrapper,\n resolves promise when html document is ready +* @public * @return {Promise} */ export function waitReady() { @@ -108,7 +118,7 @@

    Util.ts

    resolve(true); } // Otherwise, wait until document is loaded - document.addEventListener('DOMContentLoaded', resolve, false); + promQueue.push(resolve); }); } diff --git a/docs/WEAS.html b/docs/WEAS.html index 4dd15af..798d080 100644 --- a/docs/WEAS.html +++ b/docs/WEAS.html @@ -66,7 +66,7 @@ @@ -171,7 +171,7 @@

    View Source - WEAS.ts, line 88 + WEAS.ts, line 99

    @@ -242,11 +242,11 @@

    Members

    -

    - # +

    + # - children + _internal_children

    @@ -273,7 +273,7 @@

    Overrides:
    @@ -302,7 +302,275 @@

    View Source - CComponent.ts, line 16 + CComponent.ts, line 12 + +

    + +

    + + + + + + + +
    + +

    + # + + + inBuff + + +

    + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 110 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + inBuff + + +

    + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 396 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + lastAudio + + +

    + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 106 + +

    + +
    + + + + + +
    + +
    + + + + +WEAudio + + + + +

    + # + + + lastAudio + + +

    + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 387

    @@ -328,9 +596,83 @@

    -
    - main Settings, need to be overwritten with Specific settings + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 108 + +

    + +
    + + + + +
    + +
    + + + + +WEASettings + + + + +

    + # + + + settings + + +

    + + + @@ -374,9 +716,9 @@

    - View Source + View Source - CComponent.ts, line 12 + WEAS.ts, line 392

    @@ -547,7 +889,7 @@

    Parameters:

    View Source - CComponent.ts, line 27 + CComponent.ts, line 29

    @@ -665,7 +1007,7 @@

    View Source - WEAS.ts, line 237 + WEAS.ts, line 322

    @@ -792,7 +1134,104 @@

    View Source - CComponent.ts, line 41 + CComponent.ts, line 45 + +

    + +

    + + + + + + + + + + + + + + + + + + + + + +
    + + @@ -99,41 +99,67 @@

    WEAS.ts

    import {waitReady} from './Util'; import {Smallog} from './Smallog'; -import wascWorker from './wasc-worker/WascWorker'; -import {WascInterface} from './wasc-worker/WascInterface'; +import {wascWorker, WascInterface} from './wasc-worker'; const DAT_LEN = 128; /** * Audio processing settings +* @public * @extends {CSettings} */ export class WEASettings extends CSettings { + debugging: boolean = false; /** do audio processing? */ - public audioprocessing: boolean = true; + audioprocessing: boolean = true; // do pink-noise processing? - public equalize: boolean = true; + equalize: boolean = true; // convert to mono? - public mono_audio: boolean = true; + mono_audio: boolean = true; // invert low & high freqs? - public audio_direction: number = 0; + audio_direction: number = 0; // peak filtering - public peak_filter: number = 1; + peak_filter: number = 1; // neighbour-smoothing value - public value_smoothing: number = 2; + value_smoothing: number = 2; // time-value smoothing ratio - public audio_increase: number = 75; - public audio_decrease: number = 35; + audio_increase: number = 75; + audio_decrease: number = 25; // multipliers - public treble_multiplier: number = 0.5; - public mids_multiplier: number = 0.75; - public bass_multiplier: number = 1.8; + treble_multiplier: number = 0.5; + mids_multiplier: number = 0.75; + bass_multiplier: number = 1.8; // ignore value leveling for "silent" data - public minimum_volume: number = 0.005; + minimum_volume: number = 0.005; // use low latency audio? - public low_latency: boolean = false; + low_latency: boolean = false; + // show debug rendering? + show_canvas: boolean = true; } +/** +* Processed audio data representation +* @public +*/ +export interface WEAudio { + time: number; + ellapsed: number; + data: Float64Array; + bass: number; + mids: number; + highs: number; + sum: number; + min: number; + max: number; + average: number; + range: number; + silent: number; + intensity: number; +} + +/** +* @public +*/ const SettIDs = { equalize: 0, mono_audio: 1, @@ -148,6 +174,9 @@

    WEAS.ts

    minimum_volume: 10, }; +/** +* @public +*/ const PropIDs = { bass: 0, mids: 1, @@ -174,21 +203,30 @@

    WEAS.ts

    * - Wallpaper Engine Web Wallpaper environment * <br/> * - audio-processing supported web wallpaper +* @public * @extends {CComponent} */ export class WEAS extends CComponent { - // last processed audio object - public lastAudio = null; + /** @public last processed audio object */ + public lastAudio: WEAudio = null; - // settings object + /** @public settings object */ public settings: WEASettings = new WEASettings(); - // create transfer buffer - private inBuff = new Float64Array(DAT_LEN); + /** @public transfer buffer (last raw data) */ + public inBuff = new Float64Array(DAT_LEN); // web assembly functions private weasModule: WascInterface = null; + // debug render elements + private mainElm: HTMLDivElement; + private canvas1: HTMLCanvasElement; + private context1: CanvasRenderingContext2D; + private canvas2: HTMLCanvasElement; + private context2: CanvasRenderingContext2D; + private display: HTMLElement; + /** * delay audio initialization until page ready */ @@ -203,18 +241,28 @@

    WEAS.ts

    * @ignore */ private async realInit() { - // if wallpaper engine context given, listen + // only listen if wallpaper engine context given if (!window['wallpaperRegisterAudioListener']) { Smallog.info('\'window.wallpaperRegisterAudioListener\' not given!'); return; } - this.weasModule = await wascWorker('WEAS.wasm', {}, !this.settings.low_latency); - const {run} = this.weasModule; + this.injectCSS(); + this.injectHTML(); + + this.weasModule = await wascWorker('WEAS.wasm', 4096, {}, !this.settings.low_latency); + + // assert + if (this.weasModule) Smallog.debug('Got Audio provider!'); + else { + Smallog.error('Could not create WebAssembly Audio provider! [Null-Object]'); + return; + } // pass settings to module await this.updateSettings(); + const {run} = this.weasModule; const self = this; // register audio callback on module @@ -227,11 +275,11 @@

    WEAS.ts

    } // check nulls let consecutiveNull = 0; - for (let i = 0; i < 15; i++) { - const aA = audioArray[Math.floor(Math.random() * audioArray.length)]; + for (let i = 1; i < 18; i++) { + const aA = audioArray[i * 2]; if (aA == 0.0) consecutiveNull++; else consecutiveNull = 0; - if (consecutiveNull > 10) { + if (consecutiveNull > 16) { Smallog.debug('Skipping received Null data!: ' + JSON.stringify(audioArray)); return; } @@ -264,18 +312,77 @@

    WEAS.ts

    .then((result) => { const {data, props} = result; const realProps = self.getProps(props); + const teim = performance.now() - start; // apply actual last Data from worker self.lastAudio = { - time: start / 1000, + time: start, + ellapsed: teim, data, ...realProps, - }; - // print info - Smallog.debug('Got Data from Worker! Time= ' + (performance.now() - start) + ', Data= ' + JSON.stringify(realProps)); + } as any; + // print info + // Smallog.debug('Got Data from Worker! Time= ' + teim + ', Data= ' + JSON.stringify(realProps)); }); }); } + /** + * Inject preview CSS + * @ignore + */ + private injectCSS() { + const st = document.createElement('style'); + st.innerHTML = ` + #weas-preview { + opacity: 0; + position: absolute; + z-index: 2; + background: #000000aa; + } + #weas-preview canvas { + background: none; + border: white 2px dotted; + } + #weas-preview.show { + opacity: 1; + } + `; + document.head.append(st); + } + + /** + * Inject preview canvas + * @ignore + */ + private injectHTML() { + this.mainElm = document.createElement('div'); + this.mainElm.id = 'weas-preview'; + this.mainElm.innerHTML = ` + <div style="font-size: 300%"> + <span>Raw Data:</span> + <br /> + <span style="float:left">Left</span> + <span style="float:right">Right</span> + <canvas id="WEASCanvas1" style="height: 30vh; width: 95%;"></canvas> + <span>Processed:</span> + </div> + <span id="WEASDisplay"></span> + <br /> + <canvas id="WEASCanvas2" style="height: 30vh; width: 95%;"></canvas> + `; + document.body.append(this.mainElm); + + this.canvas1 = (document.getElementById('WEASCanvas1') as HTMLCanvasElement); + this.canvas1.width = window.innerWidth; + this.context1 = this.canvas1.getContext('2d'); + + this.canvas2 = (document.getElementById('WEASCanvas2') as HTMLCanvasElement); + this.canvas2.width = window.innerWidth; + this.context2 = this.canvas2.getContext('2d'); + + this.display = document.getElementById('WEASDisplay'); + } + /** * converts calculated output property number-array to string-associative-array * @param {ArrayLike<number>} dProps processed properties @@ -296,10 +403,21 @@

    WEAS.ts

    * !! CAVEAT: only available after init and module load !! * <br/> * Will send the processing settings to the WebAssembly module + * @public * @return {Promise} finished event */ public updateSettings(): Promise<void> { if (!this.weasModule) return; + + // show or hide debug info + if (this.settings.debugging) { + if (!this.mainElm.classList.contains('show')) { + this.mainElm.classList.add('show'); + } + } else { + this.mainElm.classList.remove('show'); + } + const {run} = this.weasModule; return new Promise((resolve) => { @@ -322,7 +440,7 @@

    WEAS.ts

    }) // Back to main context .then(() => { - Smallog.info('Sent Settings to WEAS: ' + JSON.stringify(sett)); + Smallog.debug('Sent Settings to WEAS: ' + JSON.stringify(sett)); resolve(); }); }); @@ -338,11 +456,52 @@

    WEAS.ts

    * - the data is silent * <br/> * - data is too old (> 3s) + * @public */ public hasAudio() { + if (this.settings.show_canvas) this.updateCanvas(); + return this.settings.audioprocessing && this.lastAudio && this.lastAudio.silent == 0 && - (performance.now() / 1000 - this.lastAudio.time < 3); + (performance.now() - this.lastAudio.time < 3000); + } + + /** + * Update the debug canvas + */ + private updateCanvas() { + // update "raw" canvas + + // clear the intersection + this.context1.clearRect(0, 0, this.canvas1.width, this.canvas1.height); + this.context1.fillStyle = 'rgb(255,0,0)'; + const barWidth = Math.round(1.0 / this.inBuff.length * this.canvas1.width); + const halfCount = this.inBuff.length / 2; + for (let i = 0; i < halfCount; ++i) { + const height = this.canvas1.height * this.inBuff[i] / 2; + this.context1.fillRect(barWidth * i, this.canvas1.height - height, barWidth, height); + } + this.context1.fillStyle = 'rgb(0,0,255)'; + for (let i = halfCount; i < this.inBuff.length; ++i) { + const height = this.canvas1.height * this.inBuff[191 - i] / 2; + this.context1.fillRect(barWidth * i, this.canvas1.height - height, barWidth, height); + } + + // update "processed" data + const tmpData = Object.assign({}, this.lastAudio); + tmpData.data = null; + this.display.innerText = JSON.stringify(tmpData, null, '\t'); + + // update "processed" canvas + this.context2.clearRect(0, 0, this.canvas2.width, this.canvas2.height); + + if (this.lastAudio && this.lastAudio.data) { + this.context2.fillStyle = 'rgb(0,255,0)'; + for (let index = 0; index < this.lastAudio.data.length; index++) { + const height = this.canvas1.height * this.lastAudio.data[index] / 2; + this.context2.fillRect(barWidth * index, this.canvas1.height - height, barWidth, height); + } + } } } diff --git a/docs/WEASettings.html b/docs/WEASettings.html index 5f36c68..a397ecc 100644 --- a/docs/WEASettings.html +++ b/docs/WEASettings.html @@ -1,632 +1,632 @@ - - - - - - - - WEASettings - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    WEASettings

    -
    - - - - - -
    - -
    - -

    WEASettings()

    - -
    Audio processing settings
    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new WEASettings() - - -

    - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEAS.ts, line 20 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - -

    Extends

    - - - - - - - - - - - - -

    Classes

    - -
    -
    WEASettings
    -
    -
    - - - - - - - - - -
    -

    Members

    -
    - -
    - -

    - # - - - audioprocessing - - -

    - - - - -
    - do audio processing? -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEAS.ts, line 24 - -

    - -
    - - - - - -
    - -
    - - - - -boolean - - - - -

    - # - - - audioprocessing - - -

    - - - - -
    - do audio processing? -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - WEAS.ts, line 256 - -

    - -
    - - - - - -
    - -
    -
    - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - apply(key, castedValue) → {boolean} - - -

    - - - - -
    - check if a certain key exists on a (dereived) object and the value type matches -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    key - - -string - - - -
    castedValue - - -Object - - - -
    -
    - - - - - -
    - - - - - - - - -
    Overrides:
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CSettings.ts, line 27 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    success
    - - -
    - - -boolean - - -
    - -
    - - -
    -
    - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - + + + + + + + + WEASettings + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    WEASettings

    +
    + + + + + +
    + +
    + +

    WEASettings()

    + +
    Audio processing settings
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new WEASettings() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 21 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + +

    Classes

    + +
    +
    WEASettings
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + +

    + # + + + audioprocessing + + +

    + + + + +
    + do audio processing? +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 26 + +

    + +
    + + + + + +
    + +
    + + + + +boolean + + + + +

    + # + + + audioprocessing + + +

    + + + + +
    + do audio processing? +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 374 + +

    + +
    + + + + + +
    + +
    +
    + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + apply(key, castedValue) → {boolean} + + +

    + + + + +
    + check if a certain key exists on a (dereived) object. if it exists, the value type matches and the value is not already equal, apply & return true otherwise return false +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +string + + + +
    castedValue + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CSettings.ts, line 32 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    value was found & changed
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/docs/WEAudio.html b/docs/WEAudio.html new file mode 100644 index 0000000..94a93af --- /dev/null +++ b/docs/WEAudio.html @@ -0,0 +1,196 @@ + + + + + + + + WEAudio + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Interface

    +

    WEAudio

    +
    + + + + + +
    + +
    + +

    WEAudio

    + + +
    + +
    +
    + + +
    Processed audio data representation
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 375 + +

    + +
    + + + + +
    + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/WEICUE.html b/docs/WEICUE.html index cf74c8c..958b94a 100644 --- a/docs/WEICUE.html +++ b/docs/WEICUE.html @@ -66,7 +66,7 @@ @@ -222,7 +222,7 @@
    Parameters:

    View Source - WEICUE.ts, line 48 + WEICUE.ts, line 50

    @@ -293,11 +293,11 @@

    Members

    -

    - # +

    + # - children + _internal_children

    @@ -324,7 +324,7 @@

    Overrides:
    @@ -353,7 +353,7 @@

    View Source - CComponent.ts, line 16 + CComponent.ts, line 12

    @@ -427,7 +427,7 @@

    View Source - CComponent.ts, line 12 + CComponent.ts, line 17

    @@ -598,7 +598,7 @@

    Parameters:

    View Source - CComponent.ts, line 27 + CComponent.ts, line 29

    @@ -725,7 +725,7 @@

    View Source - CComponent.ts, line 41 + CComponent.ts, line 45

    @@ -873,7 +873,7 @@

    Parameters:

    View Source - WEICUE.ts, line 530 + WEICUE.ts, line 377

    @@ -975,7 +975,7 @@

    View Source - WEICUE.ts, line 434 + WEICUE.ts, line 283

    diff --git a/docs/WEICUE.ts.html b/docs/WEICUE.ts.html index 4851086..5e7363c 100644 --- a/docs/WEICUE.ts.html +++ b/docs/WEICUE.ts.html @@ -68,7 +68,7 @@

    @@ -100,14 +100,18 @@

    WEICUE.ts

    import {Smallog} from './Smallog'; import {WEAS} from './WEAS'; +const IMG_SRC = './img/icue.png'; + const ClassName: string = '[WEICUE] '; const canvasX: number = 23; const canvasY: number = 7; const WaitTime: number = 30; const Transition: number = 3; + /** * iCUE processing settings +* @public * @extends {CSettings} */ export class CUESettings extends CSettings { @@ -118,7 +122,6 @@

    WEICUE.ts

    public icue_area_height: number = 30; public icue_area_blur: number = 5; public icue_area_decay: number = 15; - public icue_area_preview: boolean = false; public icue_main_color: string = '0 0.8 0'; // AudiOrbits bg Color; used as "decay"-color aswell public main_color: string = '0 0 0'; @@ -131,6 +134,7 @@

    WEICUE.ts

    * <br/> * Uses several different methods to create * Lighting effects for Corsair ICUE devices. +* @public * @extends {CComponent} */ export class WEICUE extends CComponent { @@ -145,6 +149,9 @@

    WEICUE.ts

    private icueDevices = []; private icueInterval = null; + // preview time out + private prevTimeout = null; + // runtime values public settings: CUESettings = new CUESettings(); public isAvailable: boolean = false; @@ -161,10 +168,12 @@

    WEICUE.ts

    // Plugin handler window['wallpaperPluginListener'] = { - onPluginLoaded: function(name, version) { - Smallog.info('Plugin: ' + name + ', Version: ' + version + ' : registered.', ClassName); - if (name === 'cue') this.isAvailable = true; - if (name === 'led') this.isAvailable = true; + onPluginLoaded: (name: string, version: string) => { + const lower = name.toLocaleLowerCase(); + if (lower === 'cue' || lower === 'led') { + this.isAvailable = true; + Smallog.debug(`Plugin loaded: ${name}, v${version}`, ClassName); + } }, }; waitReady().then(() => { @@ -214,8 +223,13 @@

    WEICUE.ts

    text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5); } .cuePreview { + opacity: 0; position: absolute; - background: rgba(255, 0, 0, .3); + background: rgba(255, 0, 0, .1); + transition: all 2s; + } + .cuePreview.show { + opacity: 0.5; } `; document.head.append(st); @@ -232,178 +246,19 @@

    WEICUE.ts

    // create icon (no ref needed) const imgg = document.createElement('img'); imgg.id = 'icuelogo'; - // @TODO remove this abomination - imgg.setAttribute('src', ` - data:image/png;base64,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 - AAAASUVORK5CYII= - `); + imgg.setAttribute('src', IMG_SRC); // make text holder this.texter = document.createElement('div'); this.texter.id = 'icuetext'; + + // preview area + this.preview = document.createElement('div'); + this.preview.classList.add('cuePreview'); + this.preview.innerHTML = '<h1>This is the LED/iCUE Projection-Area preview.<br />It will hide automatically.<br /><br />You can disable LED/iCUE projection under:<br />"Settings > 💡 LED/iCUE > Projection-mode = None"</h1>'; + // append image and text this.holder.append(imgg, this.texter); - document.body.append(this.holder); + document.body.append(this.holder, this.preview); } /** @@ -527,22 +382,22 @@

    WEICUE.ts

    /** * show or hide preview + * @public * @return {Promise} finished */ public updateSettings(): Promise<void> { - const sett = this.settings; - // create preview? - if (!this.preview && sett.icue_area_preview) { - this.preview = document.createElement('div'); - this.preview.classList.add('cuePreview'); - document.body.appendChild(this.preview); + // reset timeout? + if (this.prevTimeout) { + clearTimeout(this.prevTimeout); + this.prevTimeout = null; } - // update settings or destroy - if (this.preview) { - if (!sett.icue_area_preview) { - document.body.removeChild(this.preview); - this.preview = null; - } else Object.assign(this.preview.style, this.getArea(true)); + // update / show preview + if (this.isAvailable && this.preview && this.settings.icue_mode == 1) { + Object.assign(this.preview.style, this.getArea(true)); + this.preview.classList.add('show'); + this.prevTimeout = setTimeout(() => { + this.preview.classList.remove('show'); + }, 6000); } return Promise.resolve(); } @@ -555,7 +410,7 @@

    WEICUE.ts

    private initCUE(count) { // wait for plugins if (!this.isAvailable) { - if (count < 100) setTimeout(() => this.initCUE(++count), 300); + if (count < 100) setTimeout(() => this.initCUE(++count), 150); else this.icueMessage('LED: Plugin not found!'); return; } @@ -621,6 +476,7 @@

    WEICUE.ts

    /** * copy main canvas portion to our helper + * @public * @param {HTMLCanvasElementq} mainCanvas */ public updateCanvas(mainCanvas: HTMLCanvasElement) { diff --git a/docs/WEWA.ts.html b/docs/WEWA.ts.html index c553d47..87df985 100644 --- a/docs/WEWA.ts.html +++ b/docs/WEWA.ts.html @@ -68,7 +68,7 @@ @@ -96,9 +96,9 @@

    WEWA.ts

    import {waitReady} from './Util'; import {Smallog} from './Smallog'; -import {OfflineHelper} from './offline/OfflineHelper'; +import {register, reset} from './offline/OfflineHelper'; import {CC} from 'cookieconsent'; -import {myFetch} from './wasc-worker/WascRT'; +import {myFetch} from './wasc-worker/WascUtil'; const LogHead = '[WEWWA] '; const DefLang = 'de-de'; @@ -157,6 +157,8 @@

    WEWA.ts

    * - cf longer cache policy (2d?) * - <img alt's * - <form <input <label's +* +* @public */ export class WEWWA { private project: any = null; @@ -211,7 +213,7 @@

    WEWA.ts

    }); // make the website available offline using service worker - OfflineHelper.register(document.title.replace(' ', '')).then(() => { + register(document.title.replace(' ', '')).then(() => { // continue initializing finished(); this.init(); @@ -567,19 +569,19 @@

    WEWA.ts

    const preFoot = ce('div'); preFoot.innerHTML = '<hr>'; - const reset = ce('a'); - reset.classList.add('red'); - reset.innerHTML = 'Reset ↩️'; - reset.addEventListener('click', (e) => { + const rst = ce('a'); + rst.classList.add('red'); + rst.innerHTML = 'Reset ↩️'; + rst.addEventListener('click', (e) => { if (!window.confirm('This action will clear ALL local data!\r\n\r\nAre you sure?')) { return; } - OfflineHelper.reset().then(() => { + reset().then(() => { localStorage.clear(); location = location; }); }); - preFoot.append(reset); + preFoot.append(rst); // footer with ident const footer = ce('div'); @@ -906,6 +908,7 @@

    WEWA.ts

    /** * Callback for UI-Settings changes * Will apply them to the storage and running wallaper. + * @public */ public setProperty(prop, elm) { // get the type and apply the value @@ -956,11 +959,12 @@

    WEWA.ts

    } } - // eslint-disable-next-line valid-jsdoc /** * will load the given file and return it as dataURL. * this way we can easily store whole files in the configuration & localStorage. * its not safe that this works with something else than image files. + * @param {string} url + * @param {function (data: (string | ArrayBuffer)): void} resCall * @ignore */ private loadXHRSaveLocal(url, resCall) { @@ -976,6 +980,7 @@

    WEWA.ts

    /** * Show or hide menu items based on eval condition + * @public */ public evaluateSettings() { const wewwaProps = this.project.general.properties; @@ -1046,6 +1051,7 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** * Send one or more properties to the Wallpaper + * @public */ public applyProp(prop) { const wpl = window['wallpaperPropertyListener']; @@ -1057,6 +1063,7 @@

    WEWA.ts

    // eslint-disable-next-line valid-jsdoc /** * Send paused-status to the Wallpaper + * @public */ public setPaused(val: boolean) { const wpl = window['wallpaperPropertyListener']; @@ -1108,7 +1115,7 @@

    WEWA.ts

    this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); this.source = this.ctx.createMediaStreamSource(stream); this.analyser = this.ctx.createAnalyser(); - this.analyser.smoothingTimeConstant = 0.35; + this.analyser.smoothingTimeConstant = 0.15; this.analyser.fftSize = 256; this.source.connect(this.analyser); @@ -1135,7 +1142,7 @@

    WEWA.ts

    let sIdx = 0; for (let i = 0; i < 64; i++) { stereo[i] = data[sIdx++] / 255; - stereo[127 - i] = data[sIdx++] / 255; + stereo[64 + i] = data[sIdx++] / 255; } return stereo; } @@ -1157,13 +1164,13 @@

    WEWA.ts

    // insert before marker const markr = document.getElementById('audioMarker'); - markr.parentElement.insertBefore(this.audio, markr); + markr.prepend(this.audio); this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); this.source = this.ctx.createMediaElementSource(this.audio); this.analyser = this.ctx.createAnalyser(); - this.analyser.smoothingTimeConstant = 0.35; - this.analyser.fftSize = 256; + this.analyser.smoothingTimeConstant = 0.1; + this.analyser.fftSize = 512; this.source.connect(this.ctx.destination); this.source.connect(this.analyser); @@ -1193,6 +1200,7 @@

    WEWA.ts

    /** * Stop the processing loop + * @public */ public stopAudioInterval() { window['persistAudioStream'] = null; diff --git a/docs/WEWWA.html b/docs/WEWWA.html index 80f1168..3a7ab16 100644 --- a/docs/WEWWA.html +++ b/docs/WEWWA.html @@ -66,7 +66,7 @@ @@ -229,7 +229,7 @@
    Parameters:

    View Source - WEWA.ts, line 72 + WEWA.ts, line 74

    @@ -358,7 +358,7 @@

    View Source - WEWA.ts, line 910 + WEWA.ts, line 916

    @@ -455,7 +455,7 @@

    View Source - WEWA.ts, line 842 + WEWA.ts, line 847

    @@ -552,7 +552,7 @@

    View Source - WEWA.ts, line 920 + WEWA.ts, line 927

    @@ -649,7 +649,7 @@

    View Source - WEWA.ts, line 772 + WEWA.ts, line 775

    @@ -746,7 +746,7 @@

    View Source - WEWA.ts, line 1045 + WEWA.ts, line 1053

    diff --git a/docs/WarnHelper.html b/docs/WarnHelper.html index 5ddb77c..db2d281 100644 --- a/docs/WarnHelper.html +++ b/docs/WarnHelper.html @@ -66,7 +66,7 @@ @@ -87,7 +87,7 @@

    WarnHelper

    WarnHelper()

    -
    Seizure warning display
    +
    Displays a seizure warning image centered on html for a given Time.
    @@ -171,7 +171,7 @@

    View Source - WarnHelper.ts, line 38 + WarnHelper.ts, line 33

    @@ -242,11 +242,11 @@

    Members

    -

    - # +

    + # - children + _internal_children

    @@ -273,7 +273,7 @@

    Overrides:
    @@ -302,7 +302,7 @@

    View Source - CComponent.ts, line 16 + CComponent.ts, line 12

    @@ -376,7 +376,7 @@

    View Source - CComponent.ts, line 12 + CComponent.ts, line 17

    @@ -547,7 +547,7 @@

    Parameters:

    View Source - CComponent.ts, line 27 + CComponent.ts, line 29

    @@ -669,7 +669,7 @@

    View Source - WarnHelper.ts, line 112 + WarnHelper.ts, line 106

    @@ -791,7 +791,7 @@

    View Source - WarnHelper.ts, line 91 + WarnHelper.ts, line 84

    @@ -918,7 +918,7 @@

    View Source - CComponent.ts, line 41 + CComponent.ts, line 45

    @@ -1020,7 +1020,7 @@

    View Source - WarnHelper.ts, line 128 + WarnHelper.ts, line 120

    diff --git a/docs/WarnHelper.ts.html b/docs/WarnHelper.ts.html index 1eb14b8..25effc4 100644 --- a/docs/WarnHelper.ts.html +++ b/docs/WarnHelper.ts.html @@ -68,7 +68,7 @@

    @@ -92,13 +92,6 @@

    WarnHelper.ts

    * Copyright (c) 2021 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. -* -* @description -* Displays a seizure warning image centered on html for a given Time. -* -* @todo -* - add trigger warn languages to project json -* - add trigger warn as html */ import {CComponent} from './CComponent'; @@ -106,30 +99,32 @@

    WarnHelper.ts

    import {waitReady} from './Util'; const ELM_ID = 'triggerwarn'; +const IMG_SRC = './img/triggerwarning.png'; /** -* @TODO test getting text +* Seizure display warnings +* @public * @extends {CSettings} */ class WarnSettings extends CSettings { seizure_warning: boolean = true; - seizure_text: string = '/* <!-- ## ERROR ## --> */'; - animate_seconds: number = 1; - wait_seconds: number = 10; + animate_seconds: number = 2; + wait_seconds: number = 6; } /** -* Seizure warning display +* Displays a seizure warning image centered on html for a given Time. +* @public * @extends {CComponent} */ export class WarnHelper extends CComponent { + /* + * @public + */ public settings: WarnSettings = new WarnSettings(); private element: HTMLDivElement; - // promise behind showing the warning - private showResolve: any; - /** * Create and prepare once document ready */ @@ -150,11 +145,11 @@

    WarnHelper.ts

    const st = document.createElement('style'); st.innerHTML = ` #${ELM_ID} { - object-fit: contain; - text-align: center; - max-height: 30vmax; - top: 25vmin; opacity: 0; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); transition: opacity ${this.settings.animate_seconds}s ease; } #${ELM_ID}.show { @@ -169,21 +164,15 @@

    WarnHelper.ts

    * @ignore */ private injectHTML() { - this.element = document.createElement('div'); + this.element = document.createElement('img'); this.element.id = ELM_ID; + this.element.setAttribute('src', IMG_SRC); document.body.append(this.element); } - /** - * Set html text value - * @ignore - */ - private setText() { - this.element.innerHTML = this.settings.seizure_text.replace('\r\n', '<br />'); - } - /** * Show the warning + * @public * @return {Promise} hidden again */ public show(): Promise<void> { @@ -193,19 +182,20 @@

    WarnHelper.ts

    resolve(); return; } - // wait for resolve by "Hide()" - this.showResolve = resolve; - // make text - this.setText(); // show it this.element.classList.add('show'); // wait some time - setTimeout(this.hide, this.settings.wait_seconds * 1000); + setTimeout(() => { + this.hide().then(() => { + resolve(); + }); + }, this.settings.wait_seconds * 1000); }); } /** * Hide warning + * @public * @return {Promise} hidden */ public hide(): Promise<void> { @@ -213,8 +203,6 @@

    WarnHelper.ts

    // hide it & wait this.element.classList.remove('show'); setTimeout(() => { - if (this.showResolve) this.showResolve(); - this.showResolve = null; resolve(); }, this.settings.animate_seconds * 1000); }); @@ -222,17 +210,16 @@

    WarnHelper.ts

    /** * Settings have been changed + * @public * @return {Promise} finished */ public updateSettings(): Promise<void> { - // update text - this.setText(); // fix for instantly removing the warning while it shows if (!this.settings.seizure_warning && this.element.classList.contains('show')) { return this.hide(); } // whatever - return; + return Promise.resolve(); } }; diff --git a/docs/WarnSettings.html b/docs/WarnSettings.html index dc4be1d..fc10042 100644 --- a/docs/WarnSettings.html +++ b/docs/WarnSettings.html @@ -1,477 +1,472 @@ - - - - - - - - WarnSettings - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Class

    -

    WarnSettings

    -
    - - - - - -
    - -
    - -

    WarnSettings()

    - - -
    - -
    -
    - - -
    -
    -
    -
    - Constructor -
    - - - - -

    - # - - - - new WarnSettings() - - -

    - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    To Do:
    -
    -
      -
    • test getting text
    • -
    -
    - - - -

    - View Source - - WarnHelper.ts, line 25 - -

    - -
    - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    - - -

    Extends

    - - - - - - - - - - - - - - - - - - - - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - apply(key, castedValue) → {boolean} - - -

    - - - - -
    - check if a certain key exists on a (dereived) object and the value type matches -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    key - - -string - - - -
    castedValue - - -Object - - - -
    -
    - - - - - -
    - - - - - - - - -
    Overrides:
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - CSettings.ts, line 27 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    success
    - - -
    - - -boolean - - -
    - -
    - - -
    -
    - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - + + + + + + + + WarnSettings + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    WarnSettings

    +
    + + + + + +
    + +
    + +

    WarnSettings()

    + +
    Seizure display warnings
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new WarnSettings() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WarnHelper.ts, line 20 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + apply(key, castedValue) → {boolean} + + +

    + + + + +
    + check if a certain key exists on a (dereived) object. if it exists, the value type matches and the value is not already equal, apply & return true otherwise return false +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +string + + + +
    castedValue + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CSettings.ts, line 32 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    value was found & changed
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/docs/XRHelper.html b/docs/XRHelper.html new file mode 100644 index 0000000..cf42347 --- /dev/null +++ b/docs/XRHelper.html @@ -0,0 +1,1261 @@ + + + + + + + + XRHelper + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    XRHelper

    +
    + + + + + +
    + +
    + +

    XRHelper()

    + +
    XR / VR / AR Helper class. Provides availability information and starts/stops a three-js XR session
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new XRHelper() + + +

    + + + + +
    + Get typed navigator +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/XRHelper.ts, line 20 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + +

    Classes

    + +
    +
    XRHelper
    +
    +
    + + + + + + + + + +
    +

    Members

    +
    + +
    + +

    + # + + + _internal_children + + +

    + + + + +
    + Important: Append your child objects, for settings to be applied correctly! +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 12 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + settings + + +

    + + + + +
    + main Settings, need to be overwritten with Specific settings +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 17 + +

    + +
    + + + + + +
    + +
    +
    + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + applySetting(key, value) → {boolean} + + +

    + + + + +
    + will recursively try to set a setting with type and return success
    will also flag the module as needs-Update. +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +Object + + + +
    value + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 29 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    found
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + createBtn() + + +

    + + + + +
    + Create the "Exit" Button +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/XRHelper.ts, line 33 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + async + + + + + enableSession(sessionCallback) → {Promise.<boolean>} + + +

    + + + + +
    + Trys to start a Web-XR session. if successfull, will provide functionality for leaving web-XR again. +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    sessionCallback + + +function + + + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/XRHelper.ts, line 72 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + + +
    + + +Promise.<boolean> + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + async + + + + + isSupported() → {boolean} + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/XRHelper.ts, line 60 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    whether XR is supported and available or not
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + updateAll() + + +

    + + + + +
    + DO NOT OVERWWRITE !!!
    will recursively update all needed modules after settings changes +
    + + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 45 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +

    + # + + + + updateSettings() → {Promise} + + +

    + + + + +
    + NEEDS TO BE OVERWRITTEN FOR DOING ACTIONS ON SETTINGS CHANGE.
    should usually get called automatically when needed.. no need for extra calling +
    + + + + + + + + + + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CComponent.ts, line 59 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    async commpletion event
    + + +
    + + +Promise + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/XRSettings.html b/docs/XRSettings.html new file mode 100644 index 0000000..6413c22 --- /dev/null +++ b/docs/XRSettings.html @@ -0,0 +1,479 @@ + + + + + + + + XRSettings + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Class

    +

    XRSettings

    +
    + + + + + +
    + +
    + +

    XRSettings()

    + +
    XR Settings
    + + +
    + +
    +
    + + +
    +
    +
    +
    + Constructor +
    + + + + +

    + # + + + + new XRSettings() + + +

    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + three/XRHelper.ts, line 8 + +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + +

    Classes

    + +
    +
    XRSettings
    +
    +
    + + + + + + + + + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + apply(key, castedValue) → {boolean} + + +

    + + + + +
    + check if a certain key exists on a (dereived) object. if it exists, the value type matches and the value is not already equal, apply & return true otherwise return false +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    key + + +string + + + +
    castedValue + + +Object + + + +
    +
    + + + + + +
    + + + + + + + + +
    Overrides:
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + CSettings.ts, line 32 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    value was found & changed
    + + +
    + + +boolean + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/docs/global.html b/docs/global.html index d25a3e6..5966f8b 100644 --- a/docs/global.html +++ b/docs/global.html @@ -1,972 +1,1453 @@ - - - - - - - - Global - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - -
    -
    -
    - -
    -
    -
    -

    Title

    -

    Global

    -
    - - - - - -
    - -
    - -

    - - -
    - -
    -
    - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - - - -
    - - - - - - - - - - - - - - -
    -

    Members

    -
    - -
    - -

    - # - - - LogLevel - - -

    - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    See:
    -
    - -
    - - - - - -

    - View Source - - Smallog.ts, line 35 - -

    - -
    - - - - - -
    - -
    - -

    - # - - - LogLevel[undefined] - - -

    - - - - -
    - Print only error messages -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - Smallog.ts, line 40 - -

    - -
    - - - - - -
    - -
    - -

    - # - - - LogLevel[undefined] - - -

    - - - - -
    - Print error and info messages -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - Smallog.ts, line 44 - -

    - -
    - - - - - -
    - -
    - -

    - # - - - LogLevel[undefined] - - -

    - - - - -
    - Print all messages -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - Smallog.ts, line 48 - -

    - -
    - - - - - -
    - -
    - -

    - # - - - constant - - - - offlineSchema - - -

    - - - - -
    - schema for options object -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    See:
    -
    - -
    - - - - - -

    - View Source - - offline/OfflinePlugin.js, line 23 - -

    - -
    - - - - - -
    - -
    - -

    - # - - - Smallog - - -

    - - - - -
    - Small logging util, with name/time prefix & log levels -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - Smallog.ts, line 53 - -

    - -
    - - - - - -
    - -
    -
    - - - -
    -

    Methods

    -
    - -
    - - - -

    - # - - - - getAllFiles(baseDir, subDir, arrayOfFiles) → {array} - - -

    - - - - -
    - list files recursively -
    - - - - - - - - - - -
    Parameters:
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    baseDir - - -strring - - - - start directory
    subDir - - -string - - - - sub directory
    arrayOfFiles - - -array - - - - result files
    -
    - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - offline/OfflinePlugin.js, line 48 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - -
    arrayOfFiles
    - - -
    - - -array - - -
    - -
    - - -
    -
    - - - - -
    - -
    - - - -

    - # - - - - waitReady() → {Promise} - - -

    - - - - -
    - Helper function, resolves when html document is ready -
    - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - View Source - - Util.ts, line 17 - -

    - -
    - - - - - - - - - - - - - - - - - - -
    -
    -
    - - - -
    - - -
    - - -Promise - - -
    - -
    - - -
    -
    - - - - -
    - -
    -
    - - - - - -
    - -
    - - - - -
    - - - -
    -
    -
    -
    - - - - - + + + + + + + + Global + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    + +
    +
    +
    +

    Title

    +

    Global

    +
    + + + + + +
    + +
    + +

    + + +
    + +
    +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + + + + + + + + + + +
    +

    Members

    +
    + +
    + +

    + # + + + LogLevel + + +

    + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    See:
    +
    + +
    + + + + + +

    + View Source + + Smallog.ts, line 39 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + LogLevel[undefined] + + +

    + + + + +
    + Print only error messages +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + Smallog.ts, line 44 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + LogLevel[undefined] + + +

    + + + + +
    + Print error and info messages +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + Smallog.ts, line 48 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + LogLevel[undefined] + + +

    + + + + +
    + Print all messages +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + Smallog.ts, line 52 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + constant + + + + offlineSchema + + +

    + + + + +
    + schema for options object +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    See:
    +
    + +
    + + + + + +

    + View Source + + offline/OfflinePlugin.js, line 23 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + constant + + + + PropIDs + + +

    + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 71 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + constant + + + + SettIDs + + +

    + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + WEAS.ts, line 55 + +

    + +
    + + + + + +
    + +
    + +

    + # + + + Smallog + + +

    + + + + +
    + Small logging util, with name/time prefix & log levels +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + Smallog.ts, line 58 + +

    + +
    + + + + + +
    + +
    +
    + + + +
    +

    Methods

    +
    + +
    + + + +

    + # + + + + getAllFiles(baseDir, subDir, arrayOfFiles) → {array} + + +

    + + + + +
    + list files recursively +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    baseDir + + +strring + + + + start directory
    subDir + + +string + + + + sub directory
    arrayOfFiles + + +array + + + + result files
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + offline/OfflinePlugin.js, line 48 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    arrayOfFiles
    + + +
    + + +array + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + register(name, worker, oFile) → {Promise.<boolean>} + + +

    + + + + +
    + In order to intercept ALL fetch-requests offline, the scope "/" (root) is required. when you put in in a sub-directory like "/js/", the scope is also "/js/". Then, your HTTP Server will have to send the REPONSE HEADER `service-worker-allowed: /` otherwise it will cause an ERROR in your Browser. So: Putting the ServiceWorker in root folder is easiest. obviously with webpack, this causes a problem, when you are not outputting directly into the root dir... eslint-disable-next-line require-jsdoc +
    + + + + + + + + + + +
    Parameters:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    name + + +string + + + +
    worker + + +string + + + +
    oFile + + +string + + + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + offline/OfflineHelper.ts, line 49 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + + +
    + + +Promise.<boolean> + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + reset() → {Promise.<boolean>} + + +

    + + + + +
    + unregister all service workers +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + offline/OfflineHelper.ts, line 69 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + +
    finished
    + + +
    + + +Promise.<boolean> + + +
    + +
    + + +
    +
    + + + + +
    + +
    + + + +

    + # + + + + waitReady() → {Promise} + + +

    + + + + +
    + Shorthand Document ready wrapper,\n resolves promise when html document is ready +
    + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + View Source + + Util.ts, line 26 + +

    + +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    + + + +
    + + +
    + + +Promise + + +
    + +
    + + +
    +
    + + + + +
    + +
    +
    + + + + + +
    + +
    + + + + +
    + + + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 8a76539..42de973 100644 --- a/docs/index.html +++ b/docs/index.html @@ -66,7 +66,7 @@ @@ -107,9 +107,12 @@

    is a collection of utilities, mostly usefull when creating Wallpaper Engine

    Documentation

    Dependencies / Libraries

    Features / Contents

      diff --git a/docs/offline_OfflineHelper.ts.html b/docs/offline_OfflineHelper.ts.html new file mode 100644 index 0000000..4be93d8 --- /dev/null +++ b/docs/offline_OfflineHelper.ts.html @@ -0,0 +1,203 @@ + + + + + + + + + + offline/OfflineHelper.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      offline/OfflineHelper.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author hexxone / https://hexx.one
      +*
      +* @license
      +* Copyright (c) 2021 hexxone All rights reserved.
      +* Licensed under the GNU GENERAL PUBLIC LICENSE.
      +* See LICENSE file in the project root for full license information.
      +*/
      +/* eslint-disable no-unused-vars */
      +
      +import {Smallog} from '../Smallog';
      +
      +// this should pack the serviceworker like a webwoker.
      +import OfflineWorker from 'worker-loader!./Offline';
      +
      +/**
      +* @ignore
      +* Helper class for loading and registering the ServiceWorker
      +* <br/>
      +* the workerPath is the path to the compiled OfflineWorker js file, relative from the calling html file...
      +* <br/>
      +* the "?jsonPath=" argument will give the worker a "public available" json file, consisting of a string array.
      +* <br/>
      +* the array will contain all necessary files to run the app offline.
      +* <br/>
      +* the ServiceWorker will cache these files and automatically load them if the website is ran offline.
      +* <br/>
      +* ServiceWorker is a progressive technology. Some browsers will be unsupported...
      +* @public
      +*/
      +
      +// function helper, so OfflineWorker is actually processed
      +// eslint-disable-next-line require-jsdoc
      +function DontRemove() {
      +	return new OfflineWorker();
      +};
      +
      +/**
      +* In order to intercept ALL fetch-requests offline, the scope "/" (root) is required.
      +* when you put in in a sub-directory like "/js/", the scope is also "/js/".
      +* Then, your HTTP Server will have to send the REPONSE HEADER `service-worker-allowed: /`
      +* otherwise it will cause an ERROR in your Browser. So: Putting the ServiceWorker in root folder is easiest.
      +* obviously with webpack, this causes a problem, when you are not outputting directly into the root dir...
      +* eslint-disable-next-line require-jsdoc
      +*
      +* @public
      +* @param {string} name
      +* @param {string} worker
      +* @param {string} oFile
      +* @return {Promise<boolean>}
      +*/
      +export function register(name: string, worker = 'Offline.worker.js', oFile = 'offlinefiles.json') {
      +	return new Promise(async (resolve) => {
      +		if ('serviceWorker' in navigator) {
      +			const workerPath = `${worker}?name=${name}&jsonPath=${oFile}`;
      +			await navigator.serviceWorker.register(workerPath, {scope: '/'})
      +				.then(() => Smallog.info('service-worker registration complete.', '[OfflineHelper] '),
      +					() => Smallog.error('service-worker registration failure.', '[OfflineHelper] '))
      +				.then(() => resolve(true));
      +			return true;
      +		} else {
      +			Smallog.error('not supported!', '[OfflineHelper] ');
      +			resolve(false);
      +		}
      +	});
      +}
      +
      +/**
      +* unregister all service workers
      +* @return {Promise<boolean>} finished
      +* @public
      +*/
      +export async function reset() {
      +	return new Promise((resolve) => {
      +		if ('serviceWorker' in navigator) {
      +			navigator.serviceWorker.getRegistrations().then(async (registrations) => {
      +				for (const registration of registrations) {
      +					await registration.unregister();
      +				}
      +				resolve(true);
      +			});
      +		} else {
      +			Smallog.error('not supported!', '[OfflineHelper] ');
      +			resolve(false);
      +		}
      +	});
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/offline_OfflinePlugin.js.html b/docs/offline_OfflinePlugin.js.html index 4d31663..c7a8c80 100644 --- a/docs/offline_OfflinePlugin.js.html +++ b/docs/offline_OfflinePlugin.js.html @@ -68,7 +68,7 @@ diff --git a/docs/three_EffectComposer.ts.html b/docs/three_EffectComposer.ts.html new file mode 100644 index 0000000..3b860b4 --- /dev/null +++ b/docs/three_EffectComposer.ts.html @@ -0,0 +1,419 @@ + + + + + + + + + + three/EffectComposer.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/EffectComposer.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author alteredq / http://alteredqualia.com/
      +*
      +* @author hexxone / https://hexx.one
      +*/
      +
      +import {LinearFilter, PerspectiveCamera, Quaternion, RGBAFormat, Scene, Vector2, WebGLRenderer, WebGLRenderTarget, XRFrame} from 'three';
      +import {BasePass} from './pass/BasePass';
      +import {RenderPass} from './pass/RenderPass';
      +import {ShaderPass} from './pass/ShaderPass';
      +
      +const defaultParams = {
      +	minFilter: LinearFilter,
      +	magFilter: LinearFilter,
      +	format: RGBAFormat,
      +	stencilBuffer: false,
      +};
      +
      +/**
      +* render shader chain
      +* @public
      +*/
      +export class EffectComposer {
      +	private scene: Scene;
      +	private camera: PerspectiveCamera;
      +	private renderer: WebGLRenderer;
      +
      +	private varCam = new PerspectiveCamera();
      +
      +	private viewSize: Vector2;
      +
      +	private globalPrecision: string;
      +
      +	private _previousFrameTime = Date.now();
      +
      +	private defaultTarget: WebGLRenderTarget;
      +
      +	private renderWrite: WebGLRenderTarget;
      +	private writeBuffer: WebGLRenderTarget;
      +
      +	private renderRead: WebGLRenderTarget;
      +	private readBuffer: WebGLRenderTarget;
      +
      +	private normPass: RenderPass;
      +	private xrPass: RenderPass;
      +
      +	// render by default
      +	public enabled: boolean = true;
      +	public passes: BasePass[] = [];
      +
      +
      +	/**
      +	* Instantiate
      +	* @param {Scene} scene
      +	* @param {PerspectiveCamera} camera
      +	* @param {WebGLRenderer} renderer
      +	* @param {string} globalPrec
      +	* @param {WebGLRenderTarget} renderTarget
      +	*/
      +	constructor(scene: Scene, camera: PerspectiveCamera, renderer: WebGLRenderer, globalPrec: string = 'mediump', renderTarget?: WebGLRenderTarget) {
      +		this.scene = scene;
      +		this.camera = camera;
      +		this.renderer = renderer;
      +		this.viewSize = renderer.getSize(new Vector2());
      +
      +		this.varCam = new PerspectiveCamera(camera.fov, camera.aspect, camera.near, camera.far);
      +
      +		// use a new default render target if none is given
      +		this.defaultTarget = new WebGLRenderTarget(this.viewSize.width, this.viewSize.height, defaultParams);
      +		this.defaultTarget.texture.name = 'EffectComposer.dt';
      +
      +		if (renderTarget === undefined) {
      +			renderTarget = this.defaultTarget.clone();
      +			renderTarget.texture.name = 'EffectComposer.wt';
      +		}
      +
      +		// set write buffer for shader pass rendering
      +		this.renderWrite = renderTarget;
      +		this.writeBuffer = this.renderWrite;
      +
      +		// set input buffer for shader pass rendering
      +		this.renderRead = renderTarget.clone();
      +		this.renderRead.texture.name = 'EffectComposer.rt';
      +		this.readBuffer = this.renderRead;
      +
      +		this.passes = [];
      +		this._previousFrameTime = Date.now();
      +		this.globalPrecision = globalPrec;
      +
      +		this.normPass = new RenderPass(scene, camera, null, 0x000000, 1);
      +		this.xrPass = new RenderPass(scene, this.varCam, null, 0x000000, 1);
      +	}
      +
      +	/**
      +	* Append a shader to the chain
      +	* @public
      +	* @param {BasePass} pass Shader to add
      +	*/
      +	public addPass(pass: BasePass) {
      +		const p = this.wrapPrecision(pass);
      +		p.setSize(this.viewSize.width, this.viewSize.height);
      +		this.passes.push(p);
      +	}
      +
      +	/**
      +	* Insert a shader in the chain
      +	* @public
      +	* @param {BasePass} pass Shader to add
      +	* @param {number} index position
      +	*/
      +	public insertPass(pass: BasePass, index: number) {
      +		const p = this.wrapPrecision(pass);
      +		p.setSize(this.viewSize.width, this.viewSize.height);
      +		this.passes.splice(index, 0, p);
      +	}
      +
      +	/**
      +	* Checks if the given shader should be rendererd to screen
      +	* @param {number} passIndex position
      +	* @return {boolean}
      +	*/
      +	private isLastEnabledPass(passIndex: number) {
      +		for (let i = passIndex + 1; i < this.passes.length; i++) {
      +			if (this.passes[i].enabled) return false;
      +		}
      +		return true;
      +	}
      +
      +	/**
      +	* Render the shader-chain for 1 frame
      +	* @public
      +	* @param {number} deltaTime if not given, will calculate its own
      +	* @param {XRFrame} frame Currently rendering XR frame?
      +	*/
      +	public render(deltaTime?: number, frame?: XRFrame) {
      +		// deltaTime value is in seconds
      +		const dn = performance.now();
      +		if (deltaTime === undefined) {
      +			deltaTime = (dn - this._previousFrameTime) * 0.001;
      +		}
      +		this._previousFrameTime = dn;
      +		const size = new Vector2();
      +		this.renderer.getSize( size );
      +		const currentRenderTarget = this.renderer.getRenderTarget();
      +		// has enabled passes?
      +		const hasTargets = this.passes.filter((p) => p.enabled).length > 0;
      +
      +		// clear surface ?
      +		if ( this.renderer.autoClear ) this.renderer.clear();
      +
      +		// do spilt rendering
      +		if (this.renderer.xr.isPresenting && frame !== null) {
      +			this.scene.updateMatrixWorld();
      +			if ( this.camera.parent === null ) this.camera.updateMatrixWorld();
      +
      +			// update cameras
      +			const pose = frame.getViewerPose(this.renderer.xr.getReferenceSpace());
      +			const views = pose.views;
      +			const viewSize = size.width / views.length;
      +
      +			// base position
      +			const camPos = this.camera.position.clone();
      +
      +			// dont use native XR features now
      +			this.renderer.xr.enabled = false;
      +			this.renderer.setScissorTest( true );
      +
      +			// render
      +			for (let i = 0; i < views.length; i++) {
      +				const view = views[i];
      +
      +				// position
      +				const varPos = view.transform.position;
      +				this.varCam.position.set(camPos.x + varPos.x,
      +					camPos.y + (varPos.y - 1.6),
      +					camPos.z + varPos.z);
      +
      +				// orientation
      +				const vo = view.transform.orientation;
      +				this.varCam.setRotationFromQuaternion(new Quaternion(vo.x, vo.y, vo.z, vo.w));
      +
      +				// matrix
      +				this.varCam.projectionMatrix.fromArray(view.projectionMatrix);
      +				this.varCam.near = this.camera.near;
      +
      +				// render
      +				const offX = viewSize * i;
      +				this.renderer.setScissor( offX, 0, viewSize, size.height );
      +				this.renderer.setViewport( offX, 0, viewSize, size.height );
      +
      +				// pass buffers flipped to avoid swap
      +				this.xrPass.render(this.renderer, this.readBuffer, this.writeBuffer, false, !hasTargets);
      +				this.passes.forEach((pass, i) => {
      +					if (!pass.enabled) return;
      +					pass.setSize(viewSize, size.height);
      +					pass.render(this.renderer, this.writeBuffer, this.readBuffer, false, this.isLastEnabledPass(i));
      +					if (pass.needsSwap) this.swapBuffers();
      +				});
      +			}
      +
      +			// reset features
      +			this.renderer.setScissorTest( false );
      +			this.renderer.xr.enabled = true;
      +		} else {
      +			// render default
      +			this.camera.rotation.set(0, 0, 0);
      +			this.renderer.setScissor( 0, 0, size.width, size.height );
      +			this.renderer.setViewport( 0, 0, size.width, size.height );
      +			// pass buffers flipped to avoid swap
      +			this.normPass.render(this.renderer, this.readBuffer, this.writeBuffer, false, !hasTargets);
      +			this.passes.forEach((pass, i) => {
      +				if (!pass.enabled) return;
      +				pass.setSize(size.width, size.height);
      +				pass.render(this.renderer, this.writeBuffer, this.readBuffer, false, this.isLastEnabledPass(i));
      +				if (pass.needsSwap) this.swapBuffers();
      +			});
      +		}
      +
      +		this.renderer.setRenderTarget(currentRenderTarget);
      +	}
      +
      +	/**
      +	* Destroy the current shader-chain
      +	* @public
      +	* @param {WebGLRenderTarget} renderTarget target to Reset (optional)
      +	*/
      +	public reset(renderTarget?: WebGLRenderTarget) {
      +		if (renderTarget === undefined) {
      +			renderTarget = this.defaultTarget.clone();
      +			renderTarget.texture.name = 'EffectComposer.wt';
      +		}
      +
      +		this.renderWrite.dispose();
      +		this.renderRead.dispose();
      +
      +		this.renderWrite = renderTarget;
      +		this.writeBuffer = this.renderWrite;
      +
      +		this.renderRead = renderTarget.clone();
      +		this.renderRead.texture.name = 'EffectComposer.rt';
      +		this.readBuffer = this.renderRead;
      +
      +		this.passes = [];
      +
      +		this.setSize(this.viewSize.width, this.viewSize.height);
      +	}
      +
      +	/**
      +	* Updated buffer size
      +	* @public
      +	* @param {number} width X
      +	* @param {number} height Y
      +	*/
      +	public setSize(width: number, height: number) {
      +		this.renderWrite.setSize(width, height);
      +		this.renderRead.setSize(width, height);
      +		this.passes.forEach((pass) => pass.setSize(width, height));
      +		this.viewSize.set(width, height);
      +	}
      +
      +	/* UTILS */
      +
      +	/**
      +	* Prefixes custom WebGL-precision to shaders
      +	*
      +	* @param {BasePass} pass Shader to Wrap
      +	* @return {BasePass}
      +	* @ignore
      +	*/
      +	private wrapPrecision(pass: BasePass): BasePass {
      +		if (pass instanceof ShaderPass) {
      +			const copy = pass as ShaderPass;
      +			// get prefix
      +			let pre = 'precision ' + this.globalPrecision + ' float;\r\n    ' +
      +			'precision ' + this.globalPrecision + ' int;\r\n    ';
      +			// "medium" sampler precision should always be available for "high" float precision.
      +			if (this.globalPrecision == 'highp') {
      +				pre += 'precision mediump sampler2D;\r\n    ' +
      +				'precision mediump samplerCube;\r\n    ';
      +			}
      +			// apply it
      +			if (copy.material.vertexShader) {
      +				copy.material.vertexShader = pre + copy.material.vertexShader;
      +			}
      +			if (copy.material.fragmentShader) {
      +				copy.material.fragmentShader = pre + copy.material.fragmentShader;
      +			}
      +		}
      +		return pass;
      +	}
      +
      +	/**
      +	* Some shaders write to Input rather than Output...
      +	*
      +	* This is a workaround to pass their data further down the render-chain
      +	* @ignore
      +	*/
      +	private swapBuffers() {
      +		const tmp = this.readBuffer;
      +		this.readBuffer = this.writeBuffer;
      +		this.writeBuffer = tmp;
      +	}
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_XRHelper.ts.html b/docs/three_XRHelper.ts.html new file mode 100644 index 0000000..343de45 --- /dev/null +++ b/docs/three_XRHelper.ts.html @@ -0,0 +1,262 @@ + + + + + + + + + + three/XRHelper.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/XRHelper.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author hexxone / https://hexx.one
      +* @author mrdoob / http://mrdoob.com
      +* @author Mugen87 / https://github.com/Mugen87
      +*/
      +
      +import {Navigator, XRSession} from 'three';
      +import {CComponent} from '../CComponent';
      +import {CSettings} from '../CSettings';
      +
      +/**
      + * XR Settings
      + * @extends {CSettings}
      + */
      +export class XRSettings extends CSettings {
      +	xr_mode: boolean = false;
      +}
      +
      +/**
      +* XR / VR / AR Helper class.
      +* Provides availability information and starts/stops a three-js XR session
      +* @public
      +* @extends {CComponent}
      +*/
      +export class XRHelper extends CComponent {
      +	public settings: XRSettings = new XRSettings();
      +
      +	private nav: Navigator;
      +	private button: HTMLButtonElement;
      +	private currentSession: XRSession;
      +
      +	/**
      +	* Get typed navigator
      +	*/
      +	constructor() {
      +		super();
      +		this.nav = navigator as Navigator;
      +		this.createBtn();
      +	}
      +
      +	/**
      +	* Create the "Exit" Button
      +	*/
      +	private createBtn() {
      +		const btn = this.button = document.createElement('button');
      +		btn.disabled = true;
      +		btn.style.display = 'none';
      +		btn.style.position = 'absolute';
      +		btn.style.bottom = '10px';
      +		btn.style.padding = '12px 6px';
      +		btn.style.border = '1px solid #fff';
      +		btn.style.borderRadius = '4px';
      +		btn.style.background = 'rgba(0,0,0,0.1)';
      +		btn.style.color = '#fff';
      +		btn.style.font = 'normal 13px sans-serif';
      +		btn.style.textAlign = 'center';
      +		btn.style.opacity = '0.5';
      +		btn.style.outline = 'none';
      +		btn.style.zIndex = '99999';
      +
      +		btn.onmouseenter = () => {
      +			btn.style.opacity = '1.0';
      +		};
      +		btn.onmouseleave = () => {
      +			btn.style.opacity = '0.5';
      +		};
      +
      +		document.body.append(btn);
      +	}
      +
      +	/**
      +	* @return {boolean} whether XR is supported and available or not
      +	*/
      +	private async isSupported() {
      +		if ('xr' in this.nav) {
      +			return (await this.nav.xr.isSessionSupported('immersive-vr'));
      +		}
      +		return false;
      +	}
      +
      +	/**
      +	 * Trys to start a Web-XR session.
      +	* if successfull, will provide functionality for leaving web-XR again.
      +	 * @param {function (params:XRSession): void} sessionCallback
      +	 * @return {Promise<boolean>}
      +	 */
      +	public async enableSession(sessionCallback: (xrs: XRSession) => void): Promise<boolean> {
      +		return new Promise(async (resolve) => {
      +			// check availability
      +			const avail = await this.isSupported();
      +			if (!avail) {
      +				if (window.isSecureContext === false && confirm('WebXR may need HTTPS to function. Redirect?')) {
      +					document.location.href = document.location.href.replace(/^http:/, 'https:');
      +				} else {
      +					this.button.textContent = 'VR not available!';
      +					this.button.style.display = 'block';
      +					console.error('[WEBXR] Not avaiable! More info: https://immersiveweb.dev/');
      +				}
      +				resolve(false);
      +				return;
      +			}
      +
      +			this.button.textContent = 'Enter XR';
      +			this.button.style.display = 'block';
      +			this.button.disabled = false;
      +
      +			// "Toggle" style event listener
      +			this.button.addEventListener('click', async () => {
      +				if (!avail) return;
      +				// end previous session
      +				if (this.currentSession) {
      +					await this.currentSession.end();
      +					return;
      +				}
      +
      +				// WebXR's requestReferenceSpace only works if the corresponding feature
      +				// was requested at session creation time. For simplicity, just ask for
      +				// the interesting ones as optional features, but be aware that the
      +				// requestReferenceSpace call will fail if it turns out to be unavailable.
      +				// ('local' is always available for immersive sessions and doesn't need to
      +				// be requested separately.)
      +				const sessionInit = {optionalFeatures: ['local-floor']}; /* , 'bounded-floor'*/
      +				this.nav.xr.requestSession('immersive-vr', sessionInit).then((sess) => {
      +					this.currentSession = sess;
      +
      +					const lstnr = (/* event*/) => {
      +						this.currentSession.removeEventListener('end', lstnr);
      +						this.currentSession = null;
      +						this.button.textContent = 'Enter VR';
      +						sessionCallback(null);
      +					};
      +					sess.addEventListener('end', lstnr);
      +					// show exit button
      +					this.button.textContent = 'Exit VR';
      +					sessionCallback(sess);
      +				},
      +				(reason) => {
      +					console.error('[WEBXR] RequestSession failed! Reason: ' + reason);
      +				});
      +			});
      +
      +			// success
      +			resolve(true);
      +		});
      +	}
      +};
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_pass_BasePass.ts.html b/docs/three_pass_BasePass.ts.html new file mode 100644 index 0000000..b931016 --- /dev/null +++ b/docs/three_pass_BasePass.ts.html @@ -0,0 +1,143 @@ + + + + + + + + + + three/pass/BasePass.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/pass/BasePass.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author alteredq / http://alteredqualia.com/
      +* @author hexxone / https://hexx.one
      +*
      +* Basic shader pass interface
      +* @public
      +*/
      +export interface BasePass {
      +	// child name
      +	name: string;
      +
      +	// if set to true, the pass is rendered in the chain.
      +	// otherwise => ignored
      +	enabled: boolean; // = true;
      +
      +	// if set to true, the pass indicates to swap read and write buffer after rendering
      +	needsSwap: boolean; // = true;
      +
      +	// if set to true, the pass clears its buffer before rendering
      +	clear: boolean; // = false;
      +
      +	dispose();
      +
      +	setSize(width: number, height: number);
      +
      +	render(renderer: THREE.WebGLRenderer, writeBuffer: THREE.WebGLRenderTarget, readBuffer: THREE.WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean);
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_pass_FullScreenHelper.ts.html b/docs/three_pass_FullScreenHelper.ts.html new file mode 100644 index 0000000..6b96693 --- /dev/null +++ b/docs/three_pass_FullScreenHelper.ts.html @@ -0,0 +1,172 @@ + + + + + + + + + + three/pass/FullScreenHelper.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/pass/FullScreenHelper.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author alteredq / http://alteredqualia.com/
      +*
      +* @author hexxone / https://hexx.one
      +*/
      +
      +import {BufferGeometry, Camera, Material, Mesh, OrthographicCamera, PlaneBufferGeometry, WebGLRenderer} from 'three';
      +
      +/**
      +* Helper for passes that need to fill the viewport with a single quad.
      +* used to render on a PlaneGeometry ("texture")
      +* @public
      +*/
      +export class FullScreenHelper {
      +	private _mat = null;
      +
      +	public camera: Camera = null;
      +	public geometry: BufferGeometry = null;
      +	public mesh: Mesh = null;
      +
      +	/**
      +	* instantiate
      +	* @param {Material} material
      +	*/
      +	constructor(material: Material) {
      +		this._mat = material;
      +		this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
      +		this.geometry = new PlaneBufferGeometry(2, 2);
      +		this.mesh = new Mesh(this.geometry, material);
      +	}
      +
      +	/**
      +	* Change mesh material
      +	* @param {Material} mat
      +	*/
      +	public setMaterial(mat: Material) {
      +		this.mesh.material = mat;
      +	}
      +
      +	/**
      +	* Render the 2D-environment
      +	* @param {WebGLRenderer} renderer
      +	*/
      +	public render(renderer: WebGLRenderer) {
      +		renderer.render(this.mesh, this.camera);
      +	}
      +
      +	/**
      +	* Destroy 2D-environment
      +	*/
      +	public dispose() {
      +		this.camera.clear();
      +		this.mesh.clear();
      +		this.geometry.dispose();
      +	}
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_pass_RenderPass.ts.html b/docs/three_pass_RenderPass.ts.html new file mode 100644 index 0000000..33d0413 --- /dev/null +++ b/docs/three_pass_RenderPass.ts.html @@ -0,0 +1,217 @@ + + + + + + + + + + three/pass/RenderPass.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/pass/RenderPass.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author alteredq / http://alteredqualia.com/
      +*
      +* @author hexxone / https://hexx.one
      +*/
      +
      +import {Camera, Color, Material, Scene} from 'three';
      +import {BasePass} from './BasePass';
      +
      +/**
      +* Shader Render Helper
      +* @public
      +*/
      +export class RenderPass implements BasePass {
      +	name = 'RenderPass';
      +	enabled = true;
      +	needsSwap = true;
      +
      +	clear = true;
      +
      +	clearColor: Color = null;
      +	clearAlpha: number = null;
      +	clearDepth = false;
      +
      +	private scene: Scene = null;
      +	private camera: Camera = null;
      +	private overMat: Material = null;
      +
      +	/**
      +	* Construct helper
      +	* @param {Scene} scene
      +	* @param {Camera} camera
      +	* @param {Material} overMat
      +	* @param {Color} clearColor
      +	* @param {number} clearAlpha
      +	*/
      +	constructor(scene: Scene, camera: Camera, overMat: Material, clearColor, clearAlpha: number) {
      +		this.scene = scene;
      +		this.camera = camera;
      +
      +		this.overMat = overMat;
      +
      +		this.clearColor = clearColor;
      +		this.clearAlpha = (clearAlpha !== undefined) ? clearAlpha : 0;
      +	}
      +
      +	/**
      +	* Destroy shader
      +	*/
      +	public dispose() {
      +		throw new Error('Method not implemented.');
      +	}
      +
      +	/**
      +	* Updated screen size
      +	* @param {number} width X
      +	* @param {number} height Y
      +	*/
      +	public setSize(width: number, height: number) {	}
      +
      +	/**
      +	* Render Frame
      +	* @param {WebGLRenderer} renderer Context
      +	* @param {WebGLRenderTarget} writeBuffer Output
      +	* @param {WebGLRenderTarget} readBuffer Input
      +	* @param {boolean} maskActive filter
      +	* @param {boolean} renderToScreen render to canvas OR buffer
      +	* @param {Camera} camera (optional)
      +	* @public
      +	*/
      +	public render(renderer: THREE.WebGLRenderer, writeBuffer: THREE.WebGLRenderTarget, readBuffer: THREE.WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean) {
      +		const oldAutoClear = renderer.autoClear;
      +		renderer.autoClear = false;
      +
      +		this.scene.overrideMaterial = this.overMat;
      +
      +		let oldClearColor: Color;
      +		let oldClearAlpha: number;
      +
      +		if (this.clearColor) {
      +			renderer.getClearColor(oldClearColor);
      +			oldClearAlpha = renderer.getClearAlpha();
      +			renderer.setClearColor(this.clearColor, this.clearAlpha);
      +		}
      +
      +		if (this.clearDepth) {
      +			renderer.clearDepth();
      +		}
      +
      +		renderer.setRenderTarget(renderToScreen ? null : writeBuffer);
      +
      +		// TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600
      +		if (this.clear) renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil);
      +		renderer.render(this.scene, this.camera);
      +		if (this.clearColor) renderer.setClearColor(oldClearColor, oldClearAlpha);
      +
      +		this.scene.overrideMaterial = null;
      +		renderer.autoClear = oldAutoClear;
      +	}
      +}
      +
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_pass_ShaderPass.ts.html b/docs/three_pass_ShaderPass.ts.html new file mode 100644 index 0000000..9ee5803 --- /dev/null +++ b/docs/three_pass_ShaderPass.ts.html @@ -0,0 +1,218 @@ + + + + + + + + + + three/pass/ShaderPass.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/pass/ShaderPass.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author alteredq / http://alteredqualia.com/
      +*
      +* @author hexxone / https://hexx.one
      +*/
      +
      +import {ShaderMaterial, UniformsUtils, Vector2, WebGLRenderer, WebGLRenderTarget} from 'three';
      +import {BaseShader} from '../shader/BaseShader';
      +
      +import {FullScreenHelper} from './FullScreenHelper';
      +import {BasePass} from './BasePass';
      +
      +/**
      +* ThreeJS Pass for easy full screen shaders
      +* @public
      +*/
      +export class ShaderPass implements BasePass {
      +	name: string;
      +	enabled = true;
      +	needsSwap = true;
      +	clear = false;
      +	material: ShaderMaterial;
      +	textureID: string;
      +	uniforms: any;
      +	fsQuad: FullScreenHelper;
      +	iRes: Vector2;
      +
      +	/**
      +	* Make Pass
      +	* default Material will enable transparency!
      +	* @param {BaseShader|ShaderMaterial} shader Create From
      +	* @param {string} textureID Input Uniform Texture name
      +	*/
      +	constructor(shader: BaseShader | ShaderMaterial, textureID: string = 'tDiffuse') {
      +		this.textureID = textureID;
      +
      +		if (shader instanceof ShaderMaterial) {
      +			this.name = 'ShaderMaterial';
      +			this.uniforms = shader.uniforms;
      +			this.material = shader;
      +		} else if (shader) {
      +			this.name = shader.shaderID;
      +			this.uniforms = UniformsUtils.clone(shader.uniforms);
      +			this.material = new ShaderMaterial({
      +				defines: Object.assign({}, shader.defines),
      +				uniforms: this.uniforms,
      +				vertexShader: shader.vertexShader,
      +				fragmentShader: shader.fragmentShader,
      +			});
      +		}
      +		this.material.transparent = true;
      +		this.fsQuad = new FullScreenHelper(this.material);
      +	}
      +
      +	/**
      +	* Destroy Pass
      +	* @public
      +	*/
      +	public dispose() {
      +		this.fsQuad.dispose();
      +	}
      +
      +	/**
      +	* Canvas size update
      +	* @param {number} width X
      +	* @param {number} height Y
      +	* @public
      +	*/
      +	public setSize(width: number, height: number) {
      +		this.iRes = new Vector2(width, height);
      +	}
      +
      +	/**
      +	* Render frame with chaining-support
      +	* @param {WebGLRenderer} renderer
      +	* @param {WebGLRenderTarget} writeBuffer wB
      +	* @param {WebGLRenderTarget} readBuffer rB
      +	* @param {boolean} maskActive mA
      +	* @param {boolean} renderToScreen render to canvas OR buffer
      +	* @public
      +	*/
      +	public render(renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean) {
      +		if (this.uniforms[this.textureID]) {
      +			this.uniforms[this.textureID].value = readBuffer.texture;
      +		}
      +
      +		if (this.uniforms.iResolution) {
      +			this.uniforms.iResolution.value = this.iRes;
      +		}
      +
      +		this.fsQuad.setMaterial(this.material);
      +
      +		if (renderToScreen) {
      +			renderer.setRenderTarget(null);
      +		} else {
      +			renderer.setRenderTarget(writeBuffer);
      +			// TODO: Avoid using autoClear properties, see https://github.com/mrdoob/js/pull/15571#issuecomment-465669600
      +			if (this.clear) renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil);
      +		}
      +		this.fsQuad.render(renderer);
      +	}
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_pass_UnrealBloomPass.ts.html b/docs/three_pass_UnrealBloomPass.ts.html new file mode 100644 index 0000000..3349b11 --- /dev/null +++ b/docs/three_pass_UnrealBloomPass.ts.html @@ -0,0 +1,506 @@ + + + + + + + + + + three/pass/UnrealBloomPass.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/pass/UnrealBloomPass.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author spidersharma / http://eduperiment.com/
      +*/
      +
      +import {AdditiveBlending, Color, LinearFilter, MeshBasicMaterial, RGBAFormat, ShaderMaterial, UniformsUtils, Vector2, Vector3, WebGLRenderer, WebGLRenderTarget} from 'three';
      +import {FullScreenHelper} from './FullScreenHelper';
      +import {BasePass} from './BasePass';
      +
      +import {CopyShader} from '../shader/CopyShader';
      +import {LuminosityHighPassShader} from '../shader/LuminosityHighPassShader';
      +
      +/**
      +* Inspired from Unreal Engine
      +* https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/
      +*
      +* @public
      +*/
      +export class UnrealBloomPass implements BasePass {
      +	name = 'UnrealBloom';
      +	strength = null;
      +	radius = null;
      +	resolution = null;
      +	threshold = null;
      +	renderTargetBright = null;
      +	highPassUniforms = null;
      +
      +	// create color only once here, reuse it later inside the render function
      +	clear = true;
      +	clearColor = new Color(0, 0, 0);
      +	renderTargetsHorizontal: WebGLRenderTarget[] = [];
      +	renderTargetsVertical: WebGLRenderTarget[] = [];
      +	nMips = 5;
      +
      +	separableBlurMaterials: ShaderMaterial[] = [];
      +	materialHighPassFilter: ShaderMaterial = null;
      +	compositeMaterial: ShaderMaterial = null;
      +	materialCopy: ShaderMaterial = null;
      +	bloomTintColors = null;
      +	copyUniforms = null;
      +	enabled = true;
      +	needsSwap = false;
      +
      +	oldClearColor = new Color();
      +	oldClearAlpha = 1;
      +
      +	basic = new MeshBasicMaterial();
      +	fsQuad = new FullScreenHelper(null);
      +
      +	BlurDirectionX = new Vector2(1.0, 0.0);
      +	BlurDirectionY = new Vector2(0.0, 1.0);
      +
      +	/**
      +	* Construct bloom shader
      +	* @param {Vector2} resolution size
      +	* @param {number} strength multiplier
      +	* @param {number} radius size
      +	* @param {number} threshold min val
      +	*/
      +	constructor(resolution: Vector2, strength: number, radius: number, threshold: number) {
      +		this.resolution = (resolution) ? resolution : new Vector2(256, 256);
      +		this.strength = (strength !== undefined) ? strength : 1;
      +		this.radius = radius;
      +		this.threshold = threshold;
      +
      +
      +		// render targets
      +		const pars = {minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat};
      +		let resx = Math.round(this.resolution.x / 2);
      +		let resy = Math.round(this.resolution.y / 2);
      +
      +		this.renderTargetBright = new WebGLRenderTarget(resx, resy, pars);
      +		this.renderTargetBright.texture.name = 'UnrealBloomPass.bright';
      +		this.renderTargetBright.texture.generateMipmaps = false;
      +
      +		for (let i = 0; i < this.nMips; i++) {
      +			const renderTargetHorizonal = new WebGLRenderTarget(resx, resy, pars);
      +			renderTargetHorizonal.texture.name = 'UnrealBloomPass.h' + i;
      +			renderTargetHorizonal.texture.generateMipmaps = false;
      +			this.renderTargetsHorizontal.push(renderTargetHorizonal);
      +
      +			const renderTargetVertical = new WebGLRenderTarget(resx, resy, pars);
      +			renderTargetVertical.texture.name = 'UnrealBloomPass.v' + i;
      +			renderTargetVertical.texture.generateMipmaps = false;
      +			this.renderTargetsVertical.push(renderTargetVertical);
      +
      +			resx = Math.round(resx / 2);
      +			resy = Math.round(resy / 2);
      +		}
      +
      +		// luminosity high pass material
      +
      +		const highPassShader = new LuminosityHighPassShader();
      +		this.highPassUniforms = UniformsUtils.clone(highPassShader.uniforms);
      +		this.highPassUniforms['luminosityThreshold'].value = threshold;
      +		this.highPassUniforms['smoothWidth'].value = 0.01;
      +
      +		this.materialHighPassFilter = new ShaderMaterial({
      +			uniforms: this.highPassUniforms,
      +			vertexShader: highPassShader.vertexShader,
      +			fragmentShader: highPassShader.fragmentShader,
      +			defines: {},
      +		});
      +
      +		// Gaussian Blur Materials
      +		const kernelSizeArray = [3, 5, 7, 9, 11];
      +		resx = Math.round(this.resolution.x / 2);
      +		resy = Math.round(this.resolution.y / 2);
      +
      +		for (let i = 0; i < this.nMips; i++) {
      +			this.separableBlurMaterials.push(this.getSeperableBlurMaterial(kernelSizeArray[i]));
      +			this.separableBlurMaterials[i].uniforms['texSize'].value = new Vector2(resx, resy);
      +
      +			resx = Math.round(resx / 2);
      +			resy = Math.round(resy / 2);
      +		}
      +
      +		// Composite material
      +		this.compositeMaterial = this.getCompositeMaterial(this.nMips);
      +		this.compositeMaterial.uniforms['blurTexture1'].value = this.renderTargetsVertical[0].texture;
      +		this.compositeMaterial.uniforms['blurTexture2'].value = this.renderTargetsVertical[1].texture;
      +		this.compositeMaterial.uniforms['blurTexture3'].value = this.renderTargetsVertical[2].texture;
      +		this.compositeMaterial.uniforms['blurTexture4'].value = this.renderTargetsVertical[3].texture;
      +		this.compositeMaterial.uniforms['blurTexture5'].value = this.renderTargetsVertical[4].texture;
      +		this.compositeMaterial.uniforms['bloomStrength'].value = strength;
      +		this.compositeMaterial.uniforms['bloomRadius'].value = 0.1;
      +		this.compositeMaterial.needsUpdate = true;
      +
      +		const bloomFactors = [1.0, 0.8, 0.6, 0.4, 0.2];
      +		this.compositeMaterial.uniforms['bloomFactors'].value = bloomFactors;
      +		this.bloomTintColors = [new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1)];
      +		this.compositeMaterial.uniforms['bloomTintColors'].value = this.bloomTintColors;
      +
      +		// copy material
      +
      +		const copyShader = new CopyShader();
      +
      +		this.copyUniforms = UniformsUtils.clone(copyShader.uniforms);
      +		this.copyUniforms['opacity'].value = 1.0;
      +
      +		this.materialCopy = new ShaderMaterial({
      +			uniforms: this.copyUniforms,
      +			vertexShader: copyShader.vertexShader,
      +			fragmentShader: copyShader.fragmentShader,
      +			blending: AdditiveBlending,
      +			depthTest: false,
      +			depthWrite: false,
      +			transparent: true,
      +		});
      +	}
      +
      +	/**
      +	* Destroy shader
      +	*/
      +	public dispose() {
      +		for (let i = 0; i < this.renderTargetsHorizontal.length; i++) {
      +			this.renderTargetsHorizontal[i].dispose();
      +		}
      +		for (let i = 0; i < this.renderTargetsVertical.length; i++) {
      +			this.renderTargetsVertical[i].dispose();
      +		}
      +		this.renderTargetBright.dispose();
      +		this.fsQuad.dispose();
      +	}
      +
      +	/**
      +	* Updated screen size
      +	* @param {number} width X
      +	* @param {number} height Y
      +	*/
      +	public setSize(width: number, height: number) {
      +		let resx = Math.round(width / 2);
      +		let resy = Math.round(height / 2);
      +		this.renderTargetBright.setSize(resx, resy);
      +
      +		for (let i = 0; i < this.nMips; i++) {
      +			this.renderTargetsHorizontal[i].setSize(resx, resy);
      +			this.renderTargetsVertical[i].setSize(resx, resy);
      +
      +			this.separableBlurMaterials[i].uniforms['texSize'].value = new Vector2(resx, resy);
      +
      +			resx = Math.round(resx / 2);
      +			resy = Math.round(resy / 2);
      +		}
      +	}
      +
      +	/**
      +	* Render Frame
      +	* @param {WebGLRenderer} renderer Context
      +	* @param {WebGLRenderTarget} writeBuffer Output
      +	* @param {WebGLRenderTarget} readBuffer Input
      +	* @param {boolean} maskActive filter
      +	* @param {boolean} renderToScreen render to canvas OR buffer
      +	* @public
      +	*/
      +	public render(renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean) {
      +		renderer.getClearColor(this.oldClearColor);
      +		this.oldClearAlpha = renderer.getClearAlpha();
      +		const oldAutoClear = renderer.autoClear;
      +		renderer.autoClear = false;
      +
      +		renderer.setClearColor(this.clearColor, 0);
      +
      +		if (maskActive) renderer.context.disable(renderer.context.STENCIL_TEST);
      +
      +		// Render input to screen
      +		if (renderToScreen) {
      +			this.fsQuad.setMaterial(this.basic);
      +			this.basic.map = readBuffer.texture;
      +			renderer.setRenderTarget(null);
      +			renderer.clear();
      +			this.fsQuad.render(renderer);
      +		}
      +
      +		// 1. Extract Bright Areas
      +
      +		this.highPassUniforms['tDiffuse'].value = readBuffer.texture;
      +		this.highPassUniforms['luminosityThreshold'].value = this.threshold;
      +		this.fsQuad.setMaterial(this.materialHighPassFilter);
      +
      +		renderer.setRenderTarget(this.renderTargetBright);
      +		renderer.clear();
      +		this.fsQuad.render(renderer);
      +
      +		// 2. Blur All the mips progressively
      +
      +		let inputRenderTarget = this.renderTargetBright;
      +
      +		for (let i = 0; i < this.nMips; i++) {
      +			this.fsQuad.setMaterial(this.separableBlurMaterials[i]);
      +
      +			this.separableBlurMaterials[i].uniforms['colorTexture'].value = inputRenderTarget.texture;
      +			this.separableBlurMaterials[i].uniforms['direction'].value = this.BlurDirectionX;
      +			renderer.setRenderTarget(this.renderTargetsHorizontal[i]);
      +			renderer.clear();
      +			this.fsQuad.render(renderer);
      +
      +			this.separableBlurMaterials[i].uniforms['colorTexture'].value = this.renderTargetsHorizontal[i].texture;
      +			this.separableBlurMaterials[i].uniforms['direction'].value = this.BlurDirectionY;
      +			renderer.setRenderTarget(this.renderTargetsVertical[i]);
      +			renderer.clear();
      +			this.fsQuad.render(renderer);
      +
      +			inputRenderTarget = this.renderTargetsVertical[i];
      +		}
      +
      +		// Composite All the mips
      +
      +		this.fsQuad.setMaterial(this.compositeMaterial);
      +		this.compositeMaterial.uniforms['bloomStrength'].value = this.strength;
      +		this.compositeMaterial.uniforms['bloomRadius'].value = this.radius;
      +		this.compositeMaterial.uniforms['bloomTintColors'].value = this.bloomTintColors;
      +
      +		renderer.setRenderTarget(this.renderTargetsHorizontal[0]);
      +		renderer.clear();
      +		this.fsQuad.render(renderer);
      +
      +		// Blend it additively over the input texture
      +
      +		this.fsQuad.setMaterial(this.materialCopy);
      +		this.copyUniforms['tDiffuse'].value = this.renderTargetsHorizontal[0].texture;
      +
      +		if (maskActive) renderer.context.enable(renderer.context.STENCIL_TEST);
      +
      +		renderer.setRenderTarget(renderToScreen ? null : readBuffer);
      +		this.fsQuad.render(renderer);
      +
      +		// Restore renderer settings
      +		renderer.setClearColor(this.oldClearColor, this.oldClearAlpha);
      +		renderer.autoClear = oldAutoClear;
      +	}
      +
      +	/**
      +	* Make seperable material
      +	* @param {number} kernelRadius size
      +	* @return {ShaderMaterial}
      +	*/
      +	private getSeperableBlurMaterial(kernelRadius) {
      +		return new ShaderMaterial({
      +
      +			defines: {
      +				'KERNEL_RADIUS': kernelRadius,
      +				'SIGMA': kernelRadius,
      +			},
      +
      +			uniforms: {
      +				'colorTexture': {value: null},
      +				'texSize': {value: new Vector2(0.5, 0.5)},
      +				'direction': {value: new Vector2(0.5, 0.5)},
      +			},
      +
      +			vertexShader: `
      +			varying vec2 vUv;
      +			void main() {
      +				vUv = uv;
      +				gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      +			}`,
      +
      +			fragmentShader:
      +			`#include <common>
      +			
      +			varying vec2 vUv;
      +			uniform sampler2D colorTexture;
      +			uniform vec2 texSize;
      +			uniform vec2 direction;
      +			
      +			float gaussianPdf(in float x, in float sigma) {
      +				return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;
      +			}
      +			void main() {
      +				vec2 invSize = 1.0 / texSize;
      +				float fSigma = float(SIGMA);
      +				float weightSum = gaussianPdf(0.0, fSigma);
      +				float alphaSum = 0.0;
      +				vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;
      +				for( int i = 1; i < KERNEL_RADIUS; i ++ ) {
      +					float x = float(i);
      +					float w = gaussianPdf(x, fSigma);
      +					vec2 uvOffset = direction * invSize * x;
      +					vec4 sample1 = texture2D( colorTexture, vUv + uvOffset);
      +					vec4 sample2 = texture2D( colorTexture, vUv - uvOffset);
      +					diffuseSum += (sample1.rgb + sample2.rgb) * w;
      +					alphaSum += (sample1.a + sample2.a) * w;
      +					weightSum += 2.0 * w;
      +				}
      +				gl_FragColor = vec4(diffuseSum/weightSum, alphaSum/weightSum);
      +			}`,
      +		});
      +	}
      +
      +	/**
      +	* Make helper material
      +	* @param {number} nMips MipMaps
      +	* @return {ShaderMaterial}
      +	*/
      +	private getCompositeMaterial(nMips) {
      +		return new ShaderMaterial({
      +
      +			defines: {
      +				'NUM_MIPS': nMips,
      +			},
      +
      +			uniforms: {
      +				'blurTexture1': {value: null},
      +				'blurTexture2': {value: null},
      +				'blurTexture3': {value: null},
      +				'blurTexture4': {value: null},
      +				'blurTexture5': {value: null},
      +				'dirtTexture': {value: null},
      +				'bloomStrength': {value: 1.0},
      +				'bloomFactors': {value: null},
      +				'bloomTintColors': {value: null},
      +				'bloomRadius': {value: 0.0},
      +			},
      +
      +			vertexShader: `
      +			varying vec2 vUv;
      +			void main() {
      +				vUv = uv;
      +				gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      +			}`,
      +
      +			fragmentShader: `
      +			varying vec2 vUv;
      +			
      +			uniform sampler2D blurTexture1;
      +			uniform sampler2D blurTexture2;
      +			uniform sampler2D blurTexture3;
      +			uniform sampler2D blurTexture4;
      +			uniform sampler2D blurTexture5;
      +			uniform sampler2D dirtTexture;
      +			uniform float bloomStrength;
      +			uniform float bloomRadius;
      +			uniform float bloomFactors[NUM_MIPS];
      +			uniform vec3 bloomTintColors[NUM_MIPS];
      +			
      +			float lerpBloomFactor(const in float factor) {
      +				float mirrorFactor = 1.2 - factor;
      +				return mix(factor, mirrorFactor, bloomRadius);
      +			}
      +			
      +			void main() {
      +				gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + 
      +				lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + 
      +				lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + 
      +				lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + 
      +				lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) );
      +			}`,
      +		});
      +	}
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_shader_BaseShader.ts.html b/docs/three_shader_BaseShader.ts.html new file mode 100644 index 0000000..3bca83c --- /dev/null +++ b/docs/three_shader_BaseShader.ts.html @@ -0,0 +1,161 @@ + + + + + + + + + + three/shader/BaseShader.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/shader/BaseShader.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author hexxone / https://hexx.one
      +*
      +* @license
      +* Copyright (c) 2021 hexxone All rights reserved.
      +* Licensed under the GNU GENERAL PUBLIC LICENSE.
      +* See LICENSE file in the project root for full license information.
      +*/
      +
      +/**
      +* This is a basic shared interface for shaders.
      +* @public
      +*/
      +export interface BaseShader {
      +
      +	/**
      +	* short name description for the shader
      +	* @public
      +	*/
      +	shaderID: string;
      +
      +	/**
      +	* glsl vertex shader coder
      +	* @public
      +	*/
      +	vertexShader: string;
      +
      +	/**
      +	* glsl fragment shader coder
      +	* @public
      +	*/
      +	fragmentShader: string;
      +
      +	/**
      +	* glsl shared uniforms
      +	* @public
      +	*/
      +	uniforms: any;
      +
      +	/**
      +	* glsl defines
      +	* @public
      +	*/
      +	defines: any;
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_shader_BlendShader.ts.html b/docs/three_shader_BlendShader.ts.html new file mode 100644 index 0000000..0b0cde2 --- /dev/null +++ b/docs/three_shader_BlendShader.ts.html @@ -0,0 +1,166 @@ + + + + + + + + + + three/shader/BlendShader.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/shader/BlendShader.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author hexxone / https://hexx.one
      +*
      +* @license
      +* Copyright (c) 2021 hexxone All rights reserved.
      +* Licensed under the GNU GENERAL PUBLIC LICENSE.
      +* See LICENSE file in the project root for full license information.
      +*/
      +
      +import {BaseShader} from './BaseShader';
      +
      +/**
      +* Blend another texture in and out
      +* @public
      +* @implements {BaseShader}
      +*/
      +export class BlendShader implements BaseShader {
      +	defines = null;
      +
      +	shaderID = 'blendShader';
      +
      +	uniforms = {
      +		tDiffuse: {value: null},
      +		overlayBuffer: {value: null},
      +		mixValue: {value: 1},
      +	};
      +
      +	vertexShader = `
      +	varying vec2 vUv;
      +	
      +	void main() {
      +		vUv = uv;
      +		gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      +	}
      +	`;
      +
      +	fragmentShader = `
      +	uniform sampler2D tDiffuse;
      +	uniform sampler2D overlayBuffer;
      +	
      +	varying vec2 vUv;
      +	
      +	void main() {
      +		vec4 texel1 = texture2D(tDiffuse, vUv);
      +		vec4 texel2 = texture2D(overlayBuffer, vUv);
      +		vec4 diff = abs(texel1 - texel2);
      +		gl_FragColor = vec4(diff, 1.0);
      +	}
      +	`;
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_shader_BlurShader.ts.html b/docs/three_shader_BlurShader.ts.html new file mode 100644 index 0000000..f19aa13 --- /dev/null +++ b/docs/three_shader_BlurShader.ts.html @@ -0,0 +1,190 @@ + + + + + + + + + + three/shader/BlurShader.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/shader/BlurShader.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author hexxone / https://hexx.one
      +*
      +* @license
      +* Copyright (c) 2021 hexxone All rights reserved.
      +* Licensed under the GNU GENERAL PUBLIC LICENSE.
      +* See LICENSE file in the project root for full license information.
      +*/
      +
      +import {Vector2} from 'three';
      +import {BaseShader} from './BaseShader';
      +
      +/**
      +* Blur shader with Alpha support
      +* @public
      +* @implements {BaseShader}
      +*/
      +export class BlurShader implements BaseShader {
      +	defines = null;
      +
      +	shaderID = 'blurShader';
      +
      +	uniforms = {
      +		tDiffuse: {value: null},
      +		iResolution: {value: new Vector2(1, 1)},
      +		u_sigma: {value: 0.5},
      +		u_dir: {value: new Vector2(0.1, 0.1)},
      +	};
      +
      +	vertexShader = `
      +	varying vec2 vUv;
      +	
      +	void main() {
      +		vUv = uv;
      +		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      +	}
      +	`;
      +
      +	fragmentShader = `
      +	varying vec2 vUv;
      +	
      +	uniform sampler2D tDiffuse;
      +	uniform vec2 iResolution;
      +	uniform float u_sigma;
      +	uniform vec2 u_dir;
      +	
      +	float CalcGauss(float x, float sigma) {
      +		if ( sigma <= 0.0 ) return 0.0;
      +		return exp( -(x*x) / (2.0 * sigma) ) / (2.0 * 3.14157 * sigma);
      +	}
      +	
      +	void main() {
      +		vec2 texC = vUv;
      +		vec4 texCol = texture2D( tDiffuse, texC );
      +		vec4 gaussCol = vec4( texCol.rgb, 1.0 );
      +		float alphaV = texCol.a;
      +		vec2 step = u_dir / iResolution;
      +		for (int i = 1; i <= 32; ++ i)
      +		{
      +			float weight = CalcGauss(float(i) / 32.0, u_sigma * 0.5);
      +			if (weight < 1.0/255.0) break;
      +			texCol = texture2D(tDiffuse, texC + step * float(i));
      +			gaussCol += vec4(texCol.rgb * weight, weight);
      +			alphaV += texCol.a * weight;
      +			texCol = texture2D(tDiffuse, texC - step * float(i));
      +			gaussCol += vec4(texCol.rgb * weight, weight);
      +			alphaV += texCol.a * weight;
      +		}
      +		alphaV = clamp(alphaV / gaussCol.w, 0.0, 1.0);
      +		gaussCol.rgb = clamp(gaussCol.rgb / gaussCol.w, 0.0, 1.0);
      +		gl_FragColor = vec4(gaussCol.rgb, alphaV);
      +	}
      +	`;
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_shader_ChromaticShader.ts.html b/docs/three_shader_ChromaticShader.ts.html new file mode 100644 index 0000000..49d5f59 --- /dev/null +++ b/docs/three_shader_ChromaticShader.ts.html @@ -0,0 +1,176 @@ + + + + + + + + + + three/shader/ChromaticShader.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/shader/ChromaticShader.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author hexxone / https://hexx.one
      +*
      +* @license
      +* Copyright (c) 2021 hexxone All rights reserved.
      +* Licensed under the GNU GENERAL PUBLIC LICENSE.
      +* See LICENSE file in the project root for full license information.
      +*/
      +
      +import {Vector2} from 'three';
      +import {BaseShader} from './BaseShader';
      +
      +/**
      +* Chromatic Abberation shader with alpha support
      +* @public
      +* @implements {BaseShader}
      +*/
      +export class ChromaticShader implements BaseShader {
      +	defines = null;
      +
      +	shaderID = 'chromaticShader';
      +
      +	uniforms = {
      +		tDiffuse: {value: null},
      +		iResolution: {value: new Vector2(1, 1)},
      +		strength: {value: 10.0},
      +	};
      +
      +	vertexShader = `
      +	varying vec2 vUv;
      +	
      +	void main() {
      +		vUv = uv;
      +		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      +	}
      +	`;
      +
      +	fragmentShader = `
      +	uniform sampler2D tDiffuse;
      +	uniform vec2 iResolution;
      +	uniform float strength;
      +	
      +	varying vec2 vUv;
      +	
      +	vec4 ca(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) {
      +		vec4 col = vec4(0.0);
      +		vec2 off = vec2(1.33333333333333) * direction;
      +		col.ra = texture2D(image, uv).ra;
      +		col.g = texture2D(image, uv - (off / resolution)).g;
      +		col.b = texture2D(image, uv - 2. * (off / resolution)).b;
      +		return col;
      +	}
      +	
      +	void main() {
      +		vec2 uv = gl_FragCoord.xy / iResolution;
      +		vec2 direction = (uv - .5) * strength;
      +		gl_FragColor = ca(tDiffuse, uv, iResolution.xy, direction);
      +	}
      +	`;
      +};
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_shader_CopyShader.ts.html b/docs/three_shader_CopyShader.ts.html new file mode 100644 index 0000000..fdd8181 --- /dev/null +++ b/docs/three_shader_CopyShader.ts.html @@ -0,0 +1,165 @@ + + + + + + + + + + three/shader/CopyShader.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/shader/CopyShader.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author alteredq / http://alteredqualia.com/
      +* @author hexxone / https://hexx.one
      +*
      +* @license
      +* Copyright (c) 2021 hexxone All rights reserved.
      +* Licensed under the GNU GENERAL PUBLIC LICENSE.
      +* See LICENSE file in the project root for full license information.
      +*/
      +
      +import {BaseShader} from './BaseShader';
      +
      +/**
      +* Siimple I/O shader
      +* @public
      +* @implements {BaseShader}
      +*/
      +export class CopyShader implements BaseShader {
      +	defines = null;
      +
      +	shaderID = 'copyShader';
      +
      +	uniforms = {
      +		tDiffuse: {value: null},
      +		opacity: {value: 1.0},
      +	}
      +
      +	vertexShader = `
      +	varying vec2 vUv;
      +	
      +	void main() {
      +		
      +		vUv = uv;
      +		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      +	}
      +	`;
      +
      +	fragmentShader = `
      +	uniform float opacity;
      +	uniform sampler2D tDiffuse;
      +	varying vec2 vUv;
      +	
      +	void main() {
      +		
      +		vec4 texel = texture2D( tDiffuse, vUv );
      +		gl_FragColor = opacity * texel;
      +	}
      +	`;
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_shader_FXAAShader.ts.html b/docs/three_shader_FXAAShader.ts.html new file mode 100644 index 0000000..a2ad5f2 --- /dev/null +++ b/docs/three_shader_FXAAShader.ts.html @@ -0,0 +1,992 @@ + + + + + + + + + + three/shader/FXAAShader.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/shader/FXAAShader.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author alteredq / http://alteredqualia.com/
      +* @author davidedc / http://www.sketchpatch.net/
      +* @author hexxone  / https://hexx.one
      +*/
      +
      +import {Vector2} from 'three';
      +import {BaseShader} from './BaseShader';
      +
      +/**
      +* NVIDIA FXAA by Timothy Lottes
      +* http://timothylottes.blogspot.com/2011/06/fxaa3-source-released.html
      +* - WebGL port by @supereggbert
      +* http://www.glge.org/demos/fxaa/
      +*
      +* @public
      +* @implements {BaseShader}
      +*/
      +export class FXAAShader implements BaseShader {
      +	defines = null;
      +
      +	shaderID = 'fxaaShader';
      +
      +	uniforms = {
      +		tDiffuse: {value: null},
      +		resolution: {value: new Vector2(1 / 1024, 1 / 512)},
      +	}
      +
      +	vertexShader = `
      +	varying vec2 vUv;
      +	
      +	void main() {
      +		vUv = uv;
      +		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      +	}
      +	`;
      +
      +	fragmentShader = `
      +	
      +	uniform sampler2D tDiffuse;
      +	uniform vec2 resolution;
      +	varying vec2 vUv;
      +	
      +	// FXAA 3.11 implementation by NVIDIA, ported to WebGL by Agost Biro (biro@archilogic.com)
      +	//--------------
      +	// File:        es3-keplerFXAAassetsshaders/FXAA_DefaultES.frag
      +	// SDK Version: v3.00
      +	// Email:       gameworks@nvidia.com
      +	// Site:        http://developer.nvidia.com/
      +	//
      +	// Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
      +	//
      +	// Redistribution and use in source and binary forms, with or without
      +	// modification, are permitted provided that the following conditions
      +	// are met:
      +	//  * Redistributions of source code must retain the above copyright
      +	//    notice, this list of conditions and the following disclaimer.
      +	//  * Redistributions in binary form must reproduce the above copyright
      +	//    notice, this list of conditions and the following disclaimer in the
      +	//    documentation and/or other materials provided with the distribution.
      +	//  * Neither the name of NVIDIA CORPORATION nor the names of its
      +	//    contributors may be used to endorse or promote products derived
      +	//    from this software without specific prior written permission.
      +	//
      +	// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
      +	// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      +	// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      +	// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      +	// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
      +	// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      +	// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
      +	// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
      +	// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      +	// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      +	// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      +	//
      +	//--------------
      +	
      +	#define FXAA_PC 1
      +	#define FXAA_GLSL_100 1
      +	#define FXAA_QUALITY_PRESET 12
      +	#define FXAA_GREEN_AS_LUMA 1
      +	/* ===== */
      +	#ifndef FXAA_PC_CONSOLE
      +	#define FXAA_PC_CONSOLE 0
      +	#endif
      +	/* ===== */
      +	#ifndef FXAA_GLSL_120
      +	#define FXAA_GLSL_120 0
      +	#endif
      +	/* ===== */
      +	#ifndef FXAA_GLSL_130
      +	#define FXAA_GLSL_130 0
      +	#endif
      +	/* ===== */
      +	#ifndef FXAA_HLSL_3
      +	#define FXAA_HLSL_3 0
      +	#endif
      +	/* ===== */
      +	#ifndef FXAA_HLSL_4
      +	#define FXAA_HLSL_4 0
      +	#endif
      +	/* ===== */
      +	#ifndef FXAA_HLSL_5
      +	#define FXAA_HLSL_5 0
      +	#endif
      +	/* ========== */
      +	#ifndef FXAA_GREEN_AS_LUMA
      +	#define FXAA_GREEN_AS_LUMA 0
      +	#endif
      +	/* ===== */
      +	#ifndef FXAA_EARLY_EXIT
      +	#define FXAA_EARLY_EXIT 1
      +	#endif
      +	/* ===== */
      +	#ifndef FXAA_DISCARD
      +	#define FXAA_DISCARD 0
      +	#endif
      +	/* ===== */
      +	#ifndef FXAA_FAST_PIXEL_OFFSET
      +	#ifdef GL_EXT_gpu_shader4
      +	#define FXAA_FAST_PIXEL_OFFSET 1
      +	#endif
      +	#ifdef GL_NV_gpu_shader5
      +	#define FXAA_FAST_PIXEL_OFFSET 1
      +	#endif
      +	#ifdef GL_ARB_gpu_shader5
      +	#define FXAA_FAST_PIXEL_OFFSET 1
      +	#endif
      +	#ifndef FXAA_FAST_PIXEL_OFFSET
      +	#define FXAA_FAST_PIXEL_OFFSET 0
      +	#endif
      +	#endif
      +	/* ===== */
      +	#ifndef FXAA_GATHER4_ALPHA
      +	#if (FXAA_HLSL_5 == 1)
      +	#define FXAA_GATHER4_ALPHA 1
      +	#endif
      +	#ifdef GL_ARB_gpu_shader5
      +	#define FXAA_GATHER4_ALPHA 1
      +	#endif
      +	#ifdef GL_NV_gpu_shader5
      +	#define FXAA_GATHER4_ALPHA 1
      +	#endif
      +	#ifndef FXAA_GATHER4_ALPHA
      +	#define FXAA_GATHER4_ALPHA 0
      +	#endif
      +	#endif
      +	
      +	/*==========
      +	FXAA QUALITY - TUNING KNOBS
      +	----------
      +	NOTE the other tuning knobs are now in the shader function inputs!
      +	==========*/
      +	#ifndef FXAA_QUALITY_PRESET
      +	#define FXAA_QUALITY_PRESET 12
      +	#endif
      +	
      +	/*==========
      +	FXAA QUALITY - PRESETS
      +	==========*/
      +	/*==========
      +	FXAA QUALITY - MEDIUM DITHER PRESETS
      +	==========*/
      +	#if (FXAA_QUALITY_PRESET == 10)
      +	#define FXAA_QUALITY_PS 3
      +	#define FXAA_QUALITY_P0 1.5
      +	#define FXAA_QUALITY_P1 3.0
      +	#define FXAA_QUALITY_P2 12.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 11)
      +	#define FXAA_QUALITY_PS 4
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 3.0
      +	#define FXAA_QUALITY_P3 12.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 12)
      +	#define FXAA_QUALITY_PS 5
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 4.0
      +	#define FXAA_QUALITY_P4 12.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 13)
      +	#define FXAA_QUALITY_PS 6
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 2.0
      +	#define FXAA_QUALITY_P4 4.0
      +	#define FXAA_QUALITY_P5 12.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 14)
      +	#define FXAA_QUALITY_PS 7
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 2.0
      +	#define FXAA_QUALITY_P4 2.0
      +	#define FXAA_QUALITY_P5 4.0
      +	#define FXAA_QUALITY_P6 12.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 15)
      +	#define FXAA_QUALITY_PS 8
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 2.0
      +	#define FXAA_QUALITY_P4 2.0
      +	#define FXAA_QUALITY_P5 2.0
      +	#define FXAA_QUALITY_P6 4.0
      +	#define FXAA_QUALITY_P7 12.0
      +	#endif
      +	/*==========
      +	FXAA QUALITY - LOW DITHER PRESETS
      +	==========*/
      +	#if (FXAA_QUALITY_PRESET == 20)
      +	#define FXAA_QUALITY_PS 3
      +	#define FXAA_QUALITY_P0 1.5
      +	#define FXAA_QUALITY_P1 2.0
      +	#define FXAA_QUALITY_P2 8.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 21)
      +	#define FXAA_QUALITY_PS 4
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 8.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 22)
      +	#define FXAA_QUALITY_PS 5
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 2.0
      +	#define FXAA_QUALITY_P4 8.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 23)
      +	#define FXAA_QUALITY_PS 6
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 2.0
      +	#define FXAA_QUALITY_P4 2.0
      +	#define FXAA_QUALITY_P5 8.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 24)
      +	#define FXAA_QUALITY_PS 7
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 2.0
      +	#define FXAA_QUALITY_P4 2.0
      +	#define FXAA_QUALITY_P5 3.0
      +	#define FXAA_QUALITY_P6 8.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 25)
      +	#define FXAA_QUALITY_PS 8
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 2.0
      +	#define FXAA_QUALITY_P4 2.0
      +	#define FXAA_QUALITY_P5 2.0
      +	#define FXAA_QUALITY_P6 4.0
      +	#define FXAA_QUALITY_P7 8.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 26)
      +	#define FXAA_QUALITY_PS 9
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 2.0
      +	#define FXAA_QUALITY_P4 2.0
      +	#define FXAA_QUALITY_P5 2.0
      +	#define FXAA_QUALITY_P6 2.0
      +	#define FXAA_QUALITY_P7 4.0
      +	#define FXAA_QUALITY_P8 8.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 27)
      +	#define FXAA_QUALITY_PS 10
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 2.0
      +	#define FXAA_QUALITY_P4 2.0
      +	#define FXAA_QUALITY_P5 2.0
      +	#define FXAA_QUALITY_P6 2.0
      +	#define FXAA_QUALITY_P7 2.0
      +	#define FXAA_QUALITY_P8 4.0
      +	#define FXAA_QUALITY_P9 8.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 28)
      +	#define FXAA_QUALITY_PS 11
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 2.0
      +	#define FXAA_QUALITY_P4 2.0
      +	#define FXAA_QUALITY_P5 2.0
      +	#define FXAA_QUALITY_P6 2.0
      +	#define FXAA_QUALITY_P7 2.0
      +	#define FXAA_QUALITY_P8 2.0
      +	#define FXAA_QUALITY_P9 4.0
      +	#define FXAA_QUALITY_P10 8.0
      +	#endif
      +	/* ===== */
      +	#if (FXAA_QUALITY_PRESET == 29)
      +	#define FXAA_QUALITY_PS 12
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.5
      +	#define FXAA_QUALITY_P2 2.0
      +	#define FXAA_QUALITY_P3 2.0
      +	#define FXAA_QUALITY_P4 2.0
      +	#define FXAA_QUALITY_P5 2.0
      +	#define FXAA_QUALITY_P6 2.0
      +	#define FXAA_QUALITY_P7 2.0
      +	#define FXAA_QUALITY_P8 2.0
      +	#define FXAA_QUALITY_P9 2.0
      +	#define FXAA_QUALITY_P10 4.0
      +	#define FXAA_QUALITY_P11 8.0
      +	#endif
      +	/*==========
      +	FXAA QUALITY - EXTREME QUALITY
      +	==========*/
      +	#if (FXAA_QUALITY_PRESET == 39)
      +	#define FXAA_QUALITY_PS 12
      +	#define FXAA_QUALITY_P0 1.0
      +	#define FXAA_QUALITY_P1 1.0
      +	#define FXAA_QUALITY_P2 1.0
      +	#define FXAA_QUALITY_P3 1.0
      +	#define FXAA_QUALITY_P4 1.0
      +	#define FXAA_QUALITY_P5 1.5
      +	#define FXAA_QUALITY_P6 2.0
      +	#define FXAA_QUALITY_P7 2.0
      +	#define FXAA_QUALITY_P8 2.0
      +	#define FXAA_QUALITY_P9 2.0
      +	#define FXAA_QUALITY_P10 4.0
      +	#define FXAA_QUALITY_P11 8.0
      +	#endif
      +	
      +	/*==========
      +	API PORTING
      +	==========*/
      +	#if (FXAA_GLSL_100 == 1) || (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1)
      +	#define FxaaBool bool
      +	#define FxaaDiscard discard
      +	#define FxaaFloat float
      +	#define FxaaFloat2 vec2
      +	#define FxaaFloat3 vec3
      +	#define FxaaFloat4 vec4
      +	#define FxaaHalf float
      +	#define FxaaHalf2 vec2
      +	#define FxaaHalf3 vec3
      +	#define FxaaHalf4 vec4
      +	#define FxaaInt2 ivec2
      +	#define FxaaSat(x) clamp(x, 0.0, 1.0)
      +	#define FxaaTex sampler2D
      +	#else
      +	#define FxaaBool bool
      +	#define FxaaDiscard clip(-1)
      +	#define FxaaFloat float
      +	#define FxaaFloat2 float2
      +	#define FxaaFloat3 float3
      +	#define FxaaFloat4 float4
      +	#define FxaaHalf half
      +	#define FxaaHalf2 half2
      +	#define FxaaHalf3 half3
      +	#define FxaaHalf4 half4
      +	#define FxaaSat(x) saturate(x)
      +	#endif
      +	/* ===== */
      +	#if (FXAA_GLSL_100 == 1)
      +	#define FxaaTexTop(t, p) texture2D(t, p, 0.0)
      +	#define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r), 0.0)
      +	#endif
      +	/* ===== */
      +	#if (FXAA_GLSL_120 == 1)
      +	#define FxaaTexTop(t, p) texture2DLod(t, p, 0.0)
      +	#if (FXAA_FAST_PIXEL_OFFSET == 1)
      +	#define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o)
      +	#else
      +	#define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0)
      +	#endif
      +	#if (FXAA_GATHER4_ALPHA == 1)
      +	#define FxaaTexAlpha4(t, p) textureGather(t, p, 3)
      +	#define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3)
      +	#define FxaaTexGreen4(t, p) textureGather(t, p, 1)
      +	#define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1)
      +	#endif
      +	#endif
      +	/* ===== */
      +	#if (FXAA_GLSL_130 == 1)
      +	#define FxaaTexTop(t, p) textureLod(t, p, 0.0)
      +	#define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o)
      +	#if (FXAA_GATHER4_ALPHA == 1)
      +	#define FxaaTexAlpha4(t, p) textureGather(t, p, 3)
      +	#define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3)
      +	#define FxaaTexGreen4(t, p) textureGather(t, p, 1)
      +	#define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1)
      +	#endif
      +	#endif
      +	/* ===== */
      +	#if (FXAA_HLSL_3 == 1)
      +	#define FxaaInt2 float2
      +	#define FxaaTex sampler2D
      +	#define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0))
      +	#define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0))
      +	#endif
      +	/* ===== */
      +	#if (FXAA_HLSL_4 == 1)
      +	#define FxaaInt2 int2
      +	struct FxaaTex { SamplerState smpl; Texture2D tex; };
      +	#define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0)
      +	#define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o)
      +	#endif
      +	/* ===== */
      +	#if (FXAA_HLSL_5 == 1)
      +	#define FxaaInt2 int2
      +	struct FxaaTex { SamplerState smpl; Texture2D tex; };
      +	#define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0)
      +	#define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o)
      +	#define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p)
      +	#define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o)
      +	#define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p)
      +	#define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o)
      +	#endif
      +	
      +	/*==========
      +	GREEN AS LUMA OPTION SUPPORT FUNCTION
      +	==========*/
      +	#if (FXAA_GREEN_AS_LUMA == 0)
      +	FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; }
      +	#else
      +	FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; }
      +	#endif
      +	
      +	
      +	/*==========
      +	FXAA3 QUALITY - PC
      +	==========*/
      +	#if (FXAA_PC == 1)
      +	/* ===== */
      +	FxaaFloat4 FxaaPixelShader(
      +		FxaaFloat2 pos,
      +		FxaaFloat4 fxaaConsolePosPos,
      +		FxaaTex tex,
      +		FxaaTex fxaaConsole360TexExpBiasNegOne,
      +		FxaaTex fxaaConsole360TexExpBiasNegTwo,
      +		FxaaFloat2 fxaaQualityRcpFrame,
      +		FxaaFloat4 fxaaConsoleRcpFrameOpt,
      +		FxaaFloat4 fxaaConsoleRcpFrameOpt2,
      +		FxaaFloat4 fxaaConsole360RcpFrameOpt2,
      +		FxaaFloat fxaaQualitySubpix,
      +		FxaaFloat fxaaQualityEdgeThreshold,
      +		FxaaFloat fxaaQualityEdgeThresholdMin,
      +		FxaaFloat fxaaConsoleEdgeSharpness,
      +		FxaaFloat fxaaConsoleEdgeThreshold,
      +		FxaaFloat fxaaConsoleEdgeThresholdMin,
      +		FxaaFloat4 fxaaConsole360ConstDir
      +		) {
      +			/* ===== */
      +			FxaaFloat2 posM;
      +			posM.x = pos.x;
      +			posM.y = pos.y;
      +			#if (FXAA_GATHER4_ALPHA == 1)
      +			#if (FXAA_DISCARD == 0)
      +			FxaaFloat4 rgbyM = FxaaTexTop(tex, posM);
      +			#if (FXAA_GREEN_AS_LUMA == 0)
      +			#define lumaM rgbyM.w
      +			#else
      +			#define lumaM rgbyM.y
      +			#endif
      +			#endif
      +			#if (FXAA_GREEN_AS_LUMA == 0)
      +			FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM);
      +			FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1));
      +			#else
      +			FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM);
      +			FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1));
      +			#endif
      +			#if (FXAA_DISCARD == 1)
      +			#define lumaM luma4A.w
      +			#endif
      +			#define lumaE luma4A.z
      +			#define lumaS luma4A.x
      +			#define lumaSE luma4A.y
      +			#define lumaNW luma4B.w
      +			#define lumaN luma4B.z
      +			#define lumaW luma4B.x
      +			#else
      +			FxaaFloat4 rgbyM = FxaaTexTop(tex, posM);
      +			#if (FXAA_GREEN_AS_LUMA == 0)
      +			#define lumaM rgbyM.w
      +			#else
      +			#define lumaM rgbyM.y
      +			#endif
      +			#if (FXAA_GLSL_100 == 1)
      +			FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 0.0, 1.0), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0, 0.0), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 0.0,-1.0), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0, 0.0), fxaaQualityRcpFrame.xy));
      +			#else
      +			FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy));
      +			#endif
      +			#endif
      +			/* ===== */
      +			FxaaFloat maxSM = max(lumaS, lumaM);
      +			FxaaFloat minSM = min(lumaS, lumaM);
      +			FxaaFloat maxESM = max(lumaE, maxSM);
      +			FxaaFloat minESM = min(lumaE, minSM);
      +			FxaaFloat maxWN = max(lumaN, lumaW);
      +			FxaaFloat minWN = min(lumaN, lumaW);
      +			FxaaFloat rangeMax = max(maxWN, maxESM);
      +			FxaaFloat rangeMin = min(minWN, minESM);
      +			FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold;
      +			FxaaFloat range = rangeMax - rangeMin;
      +			FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled);
      +			FxaaBool earlyExit = range < rangeMaxClamped;
      +			/* ===== */
      +			if(earlyExit)
      +			#if (FXAA_DISCARD == 1)
      +			FxaaDiscard;
      +			#else
      +			return rgbyM;
      +			#endif
      +			/* ===== */
      +			#if (FXAA_GATHER4_ALPHA == 0)
      +			#if (FXAA_GLSL_100 == 1)
      +			FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0,-1.0), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0, 1.0), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0,-1.0), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0, 1.0), fxaaQualityRcpFrame.xy));
      +			#else
      +			FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy));
      +			#endif
      +			#else
      +			FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy));
      +			FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy));
      +			#endif
      +			/* ===== */
      +			FxaaFloat lumaNS = lumaN + lumaS;
      +			FxaaFloat lumaWE = lumaW + lumaE;
      +			FxaaFloat subpixRcpRange = 1.0/range;
      +			FxaaFloat subpixNSWE = lumaNS + lumaWE;
      +			FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS;
      +			FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE;
      +			/* ===== */
      +			FxaaFloat lumaNESE = lumaNE + lumaSE;
      +			FxaaFloat lumaNWNE = lumaNW + lumaNE;
      +			FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE;
      +			FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE;
      +			/* ===== */
      +			FxaaFloat lumaNWSW = lumaNW + lumaSW;
      +			FxaaFloat lumaSWSE = lumaSW + lumaSE;
      +			FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2);
      +			FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2);
      +			FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW;
      +			FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE;
      +			FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4;
      +			FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4;
      +			/* ===== */
      +			FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE;
      +			FxaaFloat lengthSign = fxaaQualityRcpFrame.x;
      +			FxaaBool horzSpan = edgeHorz >= edgeVert;
      +			FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE;
      +			/* ===== */
      +			if(!horzSpan) lumaN = lumaW;
      +			if(!horzSpan) lumaS = lumaE;
      +			if(horzSpan) lengthSign = fxaaQualityRcpFrame.y;
      +			FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM;
      +			/* ===== */
      +			FxaaFloat gradientN = lumaN - lumaM;
      +			FxaaFloat gradientS = lumaS - lumaM;
      +			FxaaFloat lumaNN = lumaN + lumaM;
      +			FxaaFloat lumaSS = lumaS + lumaM;
      +			FxaaBool pairN = abs(gradientN) >= abs(gradientS);
      +			FxaaFloat gradient = max(abs(gradientN), abs(gradientS));
      +			if(pairN) lengthSign = -lengthSign;
      +			FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange);
      +			/* ===== */
      +			FxaaFloat2 posB;
      +			posB.x = posM.x;
      +			posB.y = posM.y;
      +			FxaaFloat2 offNP;
      +			offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x;
      +			offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y;
      +			if(!horzSpan) posB.x += lengthSign * 0.5;
      +			if( horzSpan) posB.y += lengthSign * 0.5;
      +			/* ===== */
      +			FxaaFloat2 posN;
      +			posN.x = posB.x - offNP.x * FXAA_QUALITY_P0;
      +			posN.y = posB.y - offNP.y * FXAA_QUALITY_P0;
      +			FxaaFloat2 posP;
      +			posP.x = posB.x + offNP.x * FXAA_QUALITY_P0;
      +			posP.y = posB.y + offNP.y * FXAA_QUALITY_P0;
      +			FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0;
      +			FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN));
      +			FxaaFloat subpixE = subpixC * subpixC;
      +			FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP));
      +			/* ===== */
      +			if(!pairN) lumaNN = lumaSS;
      +			FxaaFloat gradientScaled = gradient * 1.0/4.0;
      +			FxaaFloat lumaMM = lumaM - lumaNN * 0.5;
      +			FxaaFloat subpixF = subpixD * subpixE;
      +			FxaaBool lumaMLTZero = lumaMM < 0.0;
      +			/* ===== */
      +			lumaEndN -= lumaNN * 0.5;
      +			lumaEndP -= lumaNN * 0.5;
      +			FxaaBool doneN = abs(lumaEndN) >= gradientScaled;
      +			FxaaBool doneP = abs(lumaEndP) >= gradientScaled;
      +			if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P1;
      +			if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P1;
      +			FxaaBool doneNP = (!doneN) || (!doneP);
      +			if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P1;
      +			if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P1;
      +			/* ===== */
      +			if(doneNP) {
      +				if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
      +				if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
      +				if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
      +				if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
      +				doneN = abs(lumaEndN) >= gradientScaled;
      +				doneP = abs(lumaEndP) >= gradientScaled;
      +				if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P2;
      +				if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P2;
      +				doneNP = (!doneN) || (!doneP);
      +				if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P2;
      +				if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P2;
      +				/* ===== */
      +				#if (FXAA_QUALITY_PS > 3)
      +				if(doneNP) {
      +					if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
      +					if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
      +					if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
      +					if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
      +					doneN = abs(lumaEndN) >= gradientScaled;
      +					doneP = abs(lumaEndP) >= gradientScaled;
      +					if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P3;
      +					if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P3;
      +					doneNP = (!doneN) || (!doneP);
      +					if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P3;
      +					if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P3;
      +					/* ===== */
      +					#if (FXAA_QUALITY_PS > 4)
      +					if(doneNP) {
      +						if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
      +						if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
      +						if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
      +						if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
      +						doneN = abs(lumaEndN) >= gradientScaled;
      +						doneP = abs(lumaEndP) >= gradientScaled;
      +						if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P4;
      +						if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P4;
      +						doneNP = (!doneN) || (!doneP);
      +						if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P4;
      +						if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P4;
      +						/* ===== */
      +						#if (FXAA_QUALITY_PS > 5)
      +						if(doneNP) {
      +							if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
      +							if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
      +							if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
      +							if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
      +							doneN = abs(lumaEndN) >= gradientScaled;
      +							doneP = abs(lumaEndP) >= gradientScaled;
      +							if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P5;
      +							if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P5;
      +							doneNP = (!doneN) || (!doneP);
      +							if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P5;
      +							if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P5;
      +							/* ===== */
      +							#if (FXAA_QUALITY_PS > 6)
      +							if(doneNP) {
      +								if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
      +								if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
      +								if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
      +								if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
      +								doneN = abs(lumaEndN) >= gradientScaled;
      +								doneP = abs(lumaEndP) >= gradientScaled;
      +								if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P6;
      +								if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P6;
      +								doneNP = (!doneN) || (!doneP);
      +								if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P6;
      +								if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P6;
      +								/* ===== */
      +								#if (FXAA_QUALITY_PS > 7)
      +								if(doneNP) {
      +									if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
      +									if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
      +									if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
      +									if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
      +									doneN = abs(lumaEndN) >= gradientScaled;
      +									doneP = abs(lumaEndP) >= gradientScaled;
      +									if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P7;
      +									if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P7;
      +									doneNP = (!doneN) || (!doneP);
      +									if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P7;
      +									if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P7;
      +									/* ===== */
      +									#if (FXAA_QUALITY_PS > 8)
      +									if(doneNP) {
      +										if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
      +										if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
      +										if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
      +										if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
      +										doneN = abs(lumaEndN) >= gradientScaled;
      +										doneP = abs(lumaEndP) >= gradientScaled;
      +										if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P8;
      +										if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P8;
      +										doneNP = (!doneN) || (!doneP);
      +										if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P8;
      +										if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P8;
      +										/* ===== */
      +										#if (FXAA_QUALITY_PS > 9)
      +										if(doneNP) {
      +											if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
      +											if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
      +											if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
      +											if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
      +											doneN = abs(lumaEndN) >= gradientScaled;
      +											doneP = abs(lumaEndP) >= gradientScaled;
      +											if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P9;
      +											if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P9;
      +											doneNP = (!doneN) || (!doneP);
      +											if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P9;
      +											if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P9;
      +											/* ===== */
      +											#if (FXAA_QUALITY_PS > 10)
      +											if(doneNP) {
      +												if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
      +												if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
      +												if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
      +												if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
      +												doneN = abs(lumaEndN) >= gradientScaled;
      +												doneP = abs(lumaEndP) >= gradientScaled;
      +												if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P10;
      +												if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P10;
      +												doneNP = (!doneN) || (!doneP);
      +												if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P10;
      +												if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P10;
      +												/* ===== */
      +												#if (FXAA_QUALITY_PS > 11)
      +												if(doneNP) {
      +													if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
      +													if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
      +													if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
      +													if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
      +													doneN = abs(lumaEndN) >= gradientScaled;
      +													doneP = abs(lumaEndP) >= gradientScaled;
      +													if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P11;
      +													if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P11;
      +													doneNP = (!doneN) || (!doneP);
      +													if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P11;
      +													if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P11;
      +													/* ===== */
      +													#if (FXAA_QUALITY_PS > 12)
      +													if(doneNP) {
      +														if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));
      +														if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));
      +														if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
      +														if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
      +														doneN = abs(lumaEndN) >= gradientScaled;
      +														doneP = abs(lumaEndP) >= gradientScaled;
      +														if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P12;
      +														if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P12;
      +														doneNP = (!doneN) || (!doneP);
      +														if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P12;
      +														if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P12;
      +														/* ===== */
      +													}
      +													#endif
      +													/* ===== */
      +												}
      +												#endif
      +												/* ===== */
      +											}
      +											#endif
      +											/* ===== */
      +										}
      +										#endif
      +										/* ===== */
      +									}
      +									#endif
      +									/* ===== */
      +								}
      +								#endif
      +								/* ===== */
      +							}
      +							#endif
      +							/* ===== */
      +						}
      +						#endif
      +						/* ===== */
      +					}
      +					#endif
      +					/* ===== */
      +				}
      +				#endif
      +				/* ===== */
      +			}
      +			/* ===== */
      +			FxaaFloat dstN = posM.x - posN.x;
      +			FxaaFloat dstP = posP.x - posM.x;
      +			if(!horzSpan) dstN = posM.y - posN.y;
      +			if(!horzSpan) dstP = posP.y - posM.y;
      +			/* ===== */
      +			FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero;
      +			FxaaFloat spanLength = (dstP + dstN);
      +			FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero;
      +			FxaaFloat spanLengthRcp = 1.0/spanLength;
      +			/* ===== */
      +			FxaaBool directionN = dstN < dstP;
      +			FxaaFloat dst = min(dstN, dstP);
      +			FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP;
      +			FxaaFloat subpixG = subpixF * subpixF;
      +			FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5;
      +			FxaaFloat subpixH = subpixG * fxaaQualitySubpix;
      +			/* ===== */
      +			FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0;
      +			FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH);
      +			if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign;
      +			if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign;
      +			#if (FXAA_DISCARD == 1)
      +			return FxaaTexTop(tex, posM);
      +			#else
      +			return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM);
      +			#endif
      +		}
      +		/* ========== */
      +		#endif
      +		void main() {
      +			gl_FragColor = FxaaPixelShader(
      +				vUv,
      +				vec4(0.0),
      +				tDiffuse,
      +				tDiffuse,
      +				tDiffuse,
      +				resolution,
      +				vec4(0.0),
      +				vec4(0.0),
      +				vec4(0.0),
      +				0.75,
      +				0.166,
      +				0.0833,
      +				0.0,
      +				0.0,
      +				0.0,
      +				vec4(0.0)
      +				);
      +				gl_FragColor.a = texture2D(tDiffuse, vUv).a;
      +			}`;
      +}
      +
      +
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_shader_FractalMirrorShader.ts.html b/docs/three_shader_FractalMirrorShader.ts.html new file mode 100644 index 0000000..18df147 --- /dev/null +++ b/docs/three_shader_FractalMirrorShader.ts.html @@ -0,0 +1,188 @@ + + + + + + + + + + three/shader/FractalMirrorShader.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/shader/FractalMirrorShader.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author hexxone / https://hexx.one
      +*
      +* @license
      +* Copyright (c) 2021 hexxone All rights reserved.
      +* Licensed under the GNU GENERAL PUBLIC LICENSE.
      +* See LICENSE file in the project root for full license information.
      +*
      +* @description
      +*/
      +
      +import {Vector2} from 'three';
      +import {BaseShader} from './BaseShader';
      +
      +/**
      +* Customized Kaleidoscope shader
      +* Inspired by ackleyrc: https://www.shadertoy.com/view/llXcRl
      +*
      +* @public
      +* @implements {BaseShader}
      +*/
      +export class FractalMirrorShader implements BaseShader {
      +	defines = null;
      +
      +	shaderID = 'fractalMirror';
      +
      +	uniforms = {
      +		tDiffuse: {value: null},
      +		iResolution: {value: new Vector2(16, 9)},
      +		numSides: {value: 2.0}, // minimum value
      +		invert: {value: false},
      +	};
      +
      +	vertexShader = `
      +	varying vec2 vUv;
      +	
      +	void main() {
      +		vUv = uv;
      +		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      +	}
      +	`;
      +
      +	fragmentShader = `
      +	uniform sampler2D tDiffuse;
      +	uniform vec2 iResolution;
      +	uniform float numSides;
      +	uniform bool invert;
      +	
      +	varying vec2 vUv;
      +	
      +	const float PI = 3.14159265358979323846;
      +	
      +	void main() {
      +		vec2 center = vec2(0.5, 0.5);
      +		float zoom = iResolution.x / iResolution.y;
      +		vec2 uv = center - vUv;
      +		if(zoom > 1.0) uv.y /= zoom;
      +		else uv.x *= zoom;
      +		
      +		float KA = PI / numSides;
      +		float angle = abs(mod(atan(uv.y, uv.x), 2.0 * KA) - KA);
      +		if(zoom > 1.0) angle -= 45.0;
      +		vec2 transformed = length(uv) * vec2(sin(angle), cos(angle));
      +		if(!invert) transformed += center;
      +		else {
      +			if(transformed.x < 0.0) transformed.x += 1.0;
      +			if(transformed.y < 0.0) transformed.y += 1.0;
      +		}
      +		gl_FragColor = texture2D(tDiffuse, transformed);
      +	}
      +	`;
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_shader_LUTShader.ts.html b/docs/three_shader_LUTShader.ts.html new file mode 100644 index 0000000..6416e55 --- /dev/null +++ b/docs/three_shader_LUTShader.ts.html @@ -0,0 +1,198 @@ + + + + + + + + + + three/shader/LUTShader.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/shader/LUTShader.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author hexxone / https://hexx.one
      +*
      +* @license
      +* Copyright (c) 2021 hexxone All rights reserved.
      +* Licensed under the GNU GENERAL PUBLIC LICENSE.
      +* See LICENSE file in the project root for full license information.
      +*/
      +
      +import {BaseShader} from './BaseShader';
      +
      +/**
      +* LookUpTable shader
      +* taken from ThreeJS examples and converted to TS
      +*
      +* @public
      +* @implements {BaseShader}
      +*/
      +export class LUTShader implements BaseShader {
      +	defines = null;
      +
      +	shaderID = 'LUTShader';
      +
      +	uniforms = {
      +		tDiffuse: {value: null},
      +		lutMap: {value: null},
      +		lutMapSize: {value: 1},
      +	};
      +
      +	vertexShader = `
      +	varying vec2 vUv;
      +	void main() {
      +		vUv = uv;
      +		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      +	}
      +	`;
      +
      +	fragmentShader = `
      +	#include <common>
      +	
      +	#define FILTER_LUT true
      +	
      +	uniform sampler2D tDiffuse;
      +	uniform sampler2D lutMap;
      +	uniform float lutMapSize;
      +	
      +	varying vec2 vUv;
      +	
      +	vec4 sampleAs3DTexture(sampler2D tex, vec3 texCoord, float size) {
      +		float sliceSize = 1.0 / size;                  // space of 1 slice
      +		float slicePixelSize = sliceSize / size;       // space of 1 pixel
      +		float width = size - 1.0;
      +		float sliceInnerSize = slicePixelSize * width; // space of size pixels
      +		float zSlice0 = floor( texCoord.z * width);
      +		float zSlice1 = min( zSlice0 + 1.0, width);
      +		float xOffset = slicePixelSize * 0.5 + texCoord.x * sliceInnerSize;
      +		float yRange = (texCoord.y * width + 0.5) / size;
      +		float s0 = xOffset + (zSlice0 * sliceSize);
      +		
      +		#ifdef FILTER_LUT
      +		
      +		float s1 = xOffset + (zSlice1 * sliceSize);
      +		vec4 slice0Color = texture2D(tex, vec2(s0, yRange));
      +		vec4 slice1Color = texture2D(tex, vec2(s1, yRange));
      +		float zOffset = mod(texCoord.z * width, 1.0);
      +		return mix(slice0Color, slice1Color, zOffset);
      +		
      +		#else
      +		
      +		return texture2D(tex, vec2( s0, yRange));
      +		
      +		#endif
      +	}
      +	
      +	void main() {
      +		vec4 originalColor = texture2D(tDiffuse, vUv);
      +		vec4 tempColor = sampleAs3DTexture(lutMap, originalColor.xyz, lutMapSize);
      +		tempColor.a = originalColor.a;
      +		gl_FragColor = tempColor;
      +	}
      +	`;
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_shader_LUTShaderNearest.ts.html b/docs/three_shader_LUTShaderNearest.ts.html new file mode 100644 index 0000000..52ea3d6 --- /dev/null +++ b/docs/three_shader_LUTShaderNearest.ts.html @@ -0,0 +1,138 @@ + + + + + + + + + + three/shader/LUTShaderNearest.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/shader/LUTShaderNearest.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author hexxone / https://hexx.one
      +*
      +* @license
      +* Copyright (c) 2021 hexxone All rights reserved.
      +* Licensed under the GNU GENERAL PUBLIC LICENSE.
      +* See LICENSE file in the project root for full license information.
      +*/
      +
      +import {LUTShader} from './LUTShader';
      +
      +/**
      +* LookUpTable shader without filtering
      +*
      +* @public
      +* @extends {LUTShader}
      +*/
      +export class LUTShaderNearest extends LUTShader {
      +	shaderID = 'LUTShaderNearest';
      +
      +	fragmentShader = new LUTShader().fragmentShader.replace('#define FILTER_LUT', '//');
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_shader_LuminosityHighPassShader.ts.html b/docs/three_shader_LuminosityHighPassShader.ts.html new file mode 100644 index 0000000..c9dd502 --- /dev/null +++ b/docs/three_shader_LuminosityHighPassShader.ts.html @@ -0,0 +1,178 @@ + + + + + + + + + + three/shader/LuminosityHighPassShader.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      three/shader/LuminosityHighPassShader.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author bhouston / http://clara.io/
      +* @author hexxone / https://hexx.one
      +*
      +* @license
      +* Copyright (c) 2021 hexxone All rights reserved.
      +* Licensed under the GNU GENERAL PUBLIC LICENSE.
      +* See LICENSE file in the project root for full license information.
      +*/
      +
      +import {Color} from 'three';
      +import {BaseShader} from './BaseShader';
      +
      +/**
      +* Luminosity
      +* http://en.wikipedia.org/wiki/Luminosity
      +*
      +* @public
      +* @implements {BaseShader}
      +*/
      +export class LuminosityHighPassShader implements BaseShader {
      +	defines = null;
      +
      +	shaderID = 'luminosityHighPass';
      +
      +	uniforms = {
      +		tDiffuse: {value: null},
      +		luminosityThreshold: {value: 1.0},
      +		smoothWidth: {value: 1.0},
      +		defaultColor: {value: new Color(0x000000)},
      +		defaultOpacity: {value: 0.0},
      +	};
      +
      +	vertexShader = `
      +	varying vec2 vUv;
      +	
      +	void main() {
      +		vUv = uv;
      +		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      +	}
      +	`;
      +
      +	fragmentShader = `
      +	uniform sampler2D tDiffuse;
      +	uniform vec3 defaultColor;
      +	uniform float defaultOpacity;
      +	uniform float luminosityThreshold;
      +	uniform float smoothWidth;
      +	
      +	varying vec2 vUv;
      +	
      +	void main() {
      +		
      +		vec4 texel = texture2D( tDiffuse, vUv );
      +		vec3 luma = vec3( 0.299, 0.587, 0.114 );
      +		float v = dot( texel.xyz, luma );
      +		vec4 outputColor = vec4( defaultColor.rgb, defaultOpacity );
      +		float alpha = smoothstep( luminosityThreshold, luminosityThreshold + smoothWidth, v );
      +		gl_FragColor = mix( outputColor, texel, alpha );
      +	}
      +	`;
      +};
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..d0d3f32 --- /dev/null +++ b/index.ts @@ -0,0 +1,26 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +export * from './src/CComponent'; +export * from './src/CSettings'; + +export * from './src/FPSta'; +export * from './src/ReloadHelper'; +export * from './src/Smallog'; +export * from './src/Util'; +export * from './src/WarnHelper'; +export * from './src/WEAS'; +export * from './src/WEICUE'; +export * from './src/WEWA'; + +export * from './src/offline/OfflineHelper'; + +export * from './src/three'; + +export * from './src/wasc-worker'; diff --git a/src/CComponent.ts b/src/CComponent.ts index 54a45fa..2efc61c 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -17,20 +17,22 @@ export class CComponent { private needsUpdate = false; /** - * main Settings, need to be overwritten with Specific settings + * Important: Append your child objects, for settings to be applied correctly! */ - public settings: CSettings = null; + _internal_children: CComponent[] = []; /** - * Important: Append your child objects, for settings to be applied correctly! + * main Settings, need to be overwritten with Specific settings + * @public */ - public children: CComponent[] = []; + public settings: CSettings = null; /** * will recursively try to set a setting with type and return success *
      * will also flag the module as needs-Update. * + * @public * @param {Object} key * @param {Object} value * @return {boolean} found @@ -41,7 +43,7 @@ export class CComponent { this.needsUpdate = true; Smallog.debug(`ApplySetting: ${key}:${value}`); } - this.children.forEach((ch) => found = (found || ch.applySetting(key, value))); + this._internal_children.forEach((ch) => found = ch.applySetting(key, value) || found); return found; } @@ -49,9 +51,11 @@ export class CComponent { * DO NOT OVERWWRITE !!! *
      * will recursively update all needed modules after settings changes + * + * @public */ - public updateAll() { - this.children.forEach((c) => c.updateAll()); + public updateAll(): void { + this._internal_children.forEach((c) => c.updateAll()); if (this.needsUpdate) this.updateSettings(); this.needsUpdate = false; } @@ -61,6 +65,7 @@ export class CComponent { *
      * should usually get called automatically when needed.. no need for extra calling * + * @public * @return {Promise} async commpletion event */ public updateSettings(): Promise { diff --git a/src/CSettings.ts b/src/CSettings.ts index 6e0823c..2e781c2 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -16,20 +16,27 @@ import {Smallog} from './Smallog'; * * All Settings-classes should be dereived from this one. * +* @public * @see {CComponent} */ export class CSettings { /** - * check if a certain key exists on a (dereived) object and the value type matches + * check if a certain key exists on a (dereived) object. + * if it exists, the value type matches and the value is not already equal, apply & return true + * otherwise return false + * + * @public * @param {string} key * @param {Object} castedValue - * @return {boolean} success + * @return {boolean} value was found & changed */ - public apply(key: string, castedValue: any) { + public apply(key: string, castedValue: any): boolean { if (this[key] !== undefined) { if (typeof this[key] === typeof castedValue) { - this[key] = castedValue; - return true; + if (this[key] !== castedValue) { + this[key] = castedValue; + return true; + } } else { Smallog.error('CSettings Error: invalid type on: \'' + key + '\'. Is: \'' + typeof this[key] + '\', applied: \'' + typeof castedValue + '\''); diff --git a/src/FPSta.ts b/src/FPSta.ts new file mode 100644 index 0000000..f3e4415 --- /dev/null +++ b/src/FPSta.ts @@ -0,0 +1,210 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +import {CComponent} from './CComponent'; +import {CSettings} from './CSettings'; +import {Smallog} from './Smallog'; +import {waitReady} from './Util'; +import {WEAS} from './WEAS'; + +const ELM_ID = 'fpstats'; + +/** +* Custom Stats settings +* @public +* @extends {CSettings} +*/ +export class FPSettings extends CSettings { + debugging : boolean = false; +} + +/** +* Custom FPS Stats module +* @public +* @extends {CComponent} +*/ +export class FPStats extends CComponent { + public settings: FPSettings = new FPSettings(); + + private container: HTMLElement; + // FPS + private fpsHolder: HTMLElement; + private lastUpdate: number = performance.now(); + private frameCount: number = 0; + // usage + private useHolder: HTMLElement; + // cpu + private cpuHolder: HTMLElement; + private cpuBegin: number = performance.now(); + private cpuEnd: number = performance.now(); + private cpuMS: number = 0; + // gpu + private gpuHolder: HTMLElement; + private gpuBegin: number = performance.now(); + private gpuEnd: number = performance.now(); + private gpuMS: number = 0; + // audio + private auProvider: WEAS = null; + private audHolder: HTMLElement; + private audioMS: number = 0; + + + /** + * Create hidden element + * @param {WEAS} audio (optional) + * @param {WEICUE} cue (optional) + */ + constructor(audio?: WEAS) { + super(); + this.auProvider = audio; + + waitReady().then(() => { + this.injectCSS(); + this.injectHTML(); + }); + } + + /** + * Make style + * @ignore + */ + private injectCSS() { + const st = document.createElement('style'); + st.innerHTML = ` + #${ELM_ID} { + opacity: 0; + position: fixed; + top: 50vh; + left: 10px; + padding: 10px; + z-index: 90000; + font-size: 1.5em; + text-align: left; + background: black; + } + #${ELM_ID}.show { + opacity: 0.8; + } + `; + document.head.append(st); + } + + /** + * Make dom + * @ignore + */ + private injectHTML() { + // root + this.container = document.createElement('div'); + this.container.id = ELM_ID; + document.body.append(this.container); + // fps + this.fpsHolder = document.createElement('div'); + this.fpsHolder.innerText = 'FPS: 0'; + // usage + this.useHolder = document.createElement('div'); + this.useHolder.innerText = 'Usage: 0 %'; + // cpu + this.cpuHolder = document.createElement('div'); + this.cpuHolder.innerText = 'CPU: 0 ms'; + // gpu + this.gpuHolder = document.createElement('div'); + this.gpuHolder.innerText = 'GPU: 0 ms'; + // append + this.container.append(this.fpsHolder, this.useHolder, this.cpuHolder, this.gpuHolder); + // audio + if (this.auProvider) { + this.audHolder = document.createElement('div'); + this.audHolder.innerText = 'Audio: 0 ms'; + this.container.append(this.audHolder); + } + } + + /** + * update visible + * @public + * @return {Promise} + */ + public updateSettings(): Promise { + // show or hide debug info + return waitReady().then(() => { + if (this.settings.debugging) this.container.classList.add('show'); + else this.container.classList.remove('show'); + }); + } + + /** + * Start measuring interval + * @public + * @param {boolean} cpu True if Cpu, false if GPU + */ + public begin(cpu: boolean) { + if (!this.settings.debugging) return; + + if (cpu) this.cpuBegin = performance.now(); + else this.gpuBegin = performance.now(); + } + + /** + * End measuring interval + * @public + * @param {boolean} cpu True if Cpu, false if GPU + */ + public end(cpu: boolean) { + if (!this.settings.debugging) return; + + if (cpu) this.cpuEnd = performance.now(); + else this.gpuEnd = performance.now(); + } + + /** + * update the html representation + * @public + */ + public update() { + this.frameCount++; + this.cpuMS += (this.cpuEnd - this.cpuBegin); + this.gpuMS += (this.gpuEnd - this.gpuBegin); + + if (this.auProvider && this.auProvider.lastAudio) { + this.audioMS = (this.audioMS + this.auProvider.lastAudio.ellapsed) / 2; + } + + // only update text ~every second + const now = performance.now(); + if (now < (this.lastUpdate + 1000)) return; + + // calculate + const elapsd = (now - this.lastUpdate) / 1000; + const efpies = this.frameCount / elapsd; + const yusage = (this.cpuMS + this.gpuMS) / 900; + const cepeyu = this.cpuMS / this.frameCount; + const gepeyu = this.gpuMS / this.frameCount; + + // apply + const da = this.fpsHolder.innerText = `FPS: ${efpies.toFixed(2)}`; + const db = this.useHolder.innerText = `Usage: ${yusage.toFixed(2)} %`; + const dc = this.cpuHolder.innerText = `CPU: ${cepeyu.toFixed(2)} ms`; + const dd = this.gpuHolder.innerText = `GPU: ${gepeyu.toFixed(2)} ms`; + Smallog.debug(`${da} ${db} ${dc} ${dd}`, '[FPStats]'); + + if (this.audHolder) this.audHolder.innerText = `Audio: ${this.audioMS.toFixed(2)} ms`; + + this.lastUpdate = now; + this.reset(); + } + + /** + * All back to 0 + * @public + */ + public reset() { + this.frameCount = this.cpuMS = this.gpuMS = this.audioMS = 0; + } +} diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index a18c521..5f93d2e 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -16,21 +16,24 @@ import {CSettings} from './CSettings'; import {waitReady} from './Util'; /** - * Reload-bar settings - */ +* Reload-bar settings +*/ class ReloadSettings extends CSettings { reload_seconds: number = 3; } /** - * Visual Reload-Bar - */ +* Visual Reload-Bar +*/ export class ReloadHelper extends CComponent { + /** + * @public + */ public settings: ReloadSettings = new ReloadSettings(); /** - * Create and prepare when document is ready - */ + * Create and prepare when document is ready + */ constructor() { super(); waitReady().then(() => { @@ -40,9 +43,9 @@ export class ReloadHelper extends CComponent { } /** - * Make custom style - * @ignore - */ + * Make custom style + * @ignore + */ private injectCSS() { const st = document.createElement('style'); st.innerHTML = ` @@ -89,9 +92,9 @@ export class ReloadHelper extends CComponent { } /** - * Make custom html elements - * @ignore - */ + * Make custom html elements + * @ignore + */ private injectHTML() { const outer = document.createElement('div'); outer.id = 'reloadhelper'; @@ -105,9 +108,10 @@ export class ReloadHelper extends CComponent { } /** - * @Todo test: bar always reset to 0 on show ?? - * @param {boolean} visible - */ + * @Todo test: bar always reset to 0 on show ?? + * @public + * @param {boolean} visible + */ public show(visible: boolean) { const e1 = document.getElementById('reload-bar'); const e2 = document.getElementById('reload-text'); @@ -121,9 +125,10 @@ export class ReloadHelper extends CComponent { } /** - * dont print IMPL error - * @return {Promise} - */ + * dont print IMPL error + * @public + * @return {Promise} + */ public updateSettings(): Promise { return Promise.resolve(); } diff --git a/src/Smallog.ts b/src/Smallog.ts index df1e223..0c7181a 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -11,12 +11,12 @@ /* eslint-disable no-unused-vars */ /** - * trace exception calls - * @param {string} def error message - * @param {number} depth which call to pick - * @return {string} - * @ignore - */ +* trace exception calls +* @param {string} def error message +* @param {number} depth which call to pick +* @return {string} +* @ignore +*/ function traceCall(def: string, depth: number = 3) { try { throw new Error('TraceCall()'); @@ -24,109 +24,98 @@ function traceCall(def: string, depth: number = 3) { // Examine e.stack here if (e.stack) { const splt = e.stack.split(/\n/); - if (splt.length > depth) return '[' + splt[depth].trim().substring(3) + '] '; + let trim = splt[depth].trim(); + if (trim.indexOf('at ') == 0) trim = trim.substring(3); + if (splt.length > depth) return '[' + trim + '] '; } } return def; } /** - * @see {Smallog} - */ +* @see {Smallog} +* @public +*/ export enum LogLevel { /** - * Print only error messages - */ + * Print only error messages + */ Error = 0, /** - * Print error and info messages - */ + * Print error and info messages + */ Info = 1, /** - * Print all messages - */ + * Print all messages + */ Debug = 2 } /** - * Small logging util, with name/time prefix & log levels - */ +* Small logging util, with name/time prefix & log levels +* @public +*/ export module Smallog { - - let logLevel: LogLevel = LogLevel.Debug; // todo level Info for release + const logLevel: LogLevel = 2; let preFix: string = '[Smallog] '; let printTime: boolean = false; /** - * get logging output level - * @return {LogLevel} current - */ + * get logging output level + * @return {LogLevel} current + */ export function getLevel() { return logLevel; } /** - * set logging output level - * @param {LogLevel} level new - */ - export function setLevel(level: LogLevel) { - logLevel = level; - } - - /** - * set logging prefix - * @param {string} pre - */ + * set logging prefix + * @param {string} pre + */ export function setPrefix(pre: string) { preFix = pre; } /** - * set time prefix - * @param {boolean} print - */ + * set time prefix + * @param {boolean} print + */ export function setPrintTime(print: boolean) { printTime = print; } /** - * print error message - * @param {string} msg log - * @param {string} hdr overwrite header - */ + * print error message + * @param {string} msg log + * @param {string} hdr overwrite header + */ export function error(msg: string, hdr: string = preFix) { - log(console.error, msg, traceCall(hdr)); + if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + console.error(hdr + msg); } /** - * print info message - * @param {string} msg log - * @param {string} hdr overwrite header - */ + * print info message + * @param {string} msg log + * @param {string} hdr overwrite header + */ export function info(msg: string, hdr: string = preFix) { - if (logLevel >= 2) hdr = traceCall(hdr); - if (logLevel >= 1) log(console.info, msg, hdr); + if (logLevel >= 1) { + if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + if (logLevel >= 2) hdr = traceCall(hdr); + console.info(hdr + msg); + } } /** - * print debug message - * @param {string} msg log - * @param {string} hdr overwrite header - */ + * print debug message + * @param {string} msg log + * @param {string} hdr overwrite header + */ export function debug(msg: string, hdr: string = preFix) { - if (logLevel >= 2) log(console.debug, msg, traceCall(hdr)); - } - - /** - * internal logging function - * @param {Function} call log callback - * @param {string} msg text to print - * @param {string} hdr header to include - * @ignore - */ - function log(call: (...data: any) => void, msg: string, hdr: string) { - let m = msg; - if (printTime) m = ('[' + new Date().toLocaleString() + '] ') + m; - call(hdr + m); + if (logLevel >= 2) { + if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + console.debug(hdr + msg); + } } } diff --git a/src/Stats.ts b/src/Stats.ts deleted file mode 100644 index d0501f5..0000000 --- a/src/Stats.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -* -* TypeScript Wrapper for mrdoob Stats.js -* Still requires Stats.js to be included! -* @ignore -*/ - -declare interface Stats { - REVISION: number; - dom: HTMLDivElement; - addPanel(panel: Stats.Panel): Stats.Panel; - showPanel(id: number): void; - begin(): void; - end(): void; - update(): void; - domElement: HTMLDivElement; - setMode(id: number): void; -} - -declare function Stats(): Stats; - -declare namespace Stats { - interface Panel { - dom: HTMLCanvasElement; - update(value: number, maxValue: number): void; - } - - // eslint-disable-next-line no-unused-vars - function Panel(name?: string, fg?: string, bg?: string): Panel; -} - -export default Stats; diff --git a/src/Util.ts b/src/Util.ts index 703ea18..96cdad3 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -6,12 +6,22 @@ * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * -* @description -* Shorthand Document ready wrapper +* @ignore */ +// promise resolve queue +const promQueue: ((val) => void)[] = []; +const workQueue = () => { + while (promQueue.length > 0) { + const call = promQueue.shift(); + call(true); + } +}; +document.addEventListener('DOMContentLoaded', workQueue, false); + /** -* Helper function, resolves when html document is ready +* Shorthand Document ready wrapper,\n resolves promise when html document is ready +* @public * @return {Promise} */ export function waitReady() { @@ -21,6 +31,6 @@ export function waitReady() { resolve(true); } // Otherwise, wait until document is loaded - document.addEventListener('DOMContentLoaded', resolve, false); + promQueue.push(resolve); }); } diff --git a/src/WEAS.ts b/src/WEAS.ts index 270458f..6c09ed5 100644 --- a/src/WEAS.ts +++ b/src/WEAS.ts @@ -12,41 +12,67 @@ import {CSettings} from './CSettings'; import {waitReady} from './Util'; import {Smallog} from './Smallog'; -import wascWorker from './wasc-worker/WascWorker'; -import {WascInterface} from './wasc-worker/WascInterface'; +import {wascWorker, WascInterface} from './wasc-worker'; const DAT_LEN = 128; /** * Audio processing settings +* @public * @extends {CSettings} */ export class WEASettings extends CSettings { + debugging: boolean = false; /** do audio processing? */ - public audioprocessing: boolean = true; + audioprocessing: boolean = true; // do pink-noise processing? - public equalize: boolean = true; + equalize: boolean = true; // convert to mono? - public mono_audio: boolean = true; + mono_audio: boolean = true; // invert low & high freqs? - public audio_direction: number = 0; + audio_direction: number = 0; // peak filtering - public peak_filter: number = 1; + peak_filter: number = 1; // neighbour-smoothing value - public value_smoothing: number = 2; + value_smoothing: number = 2; // time-value smoothing ratio - public audio_increase: number = 75; - public audio_decrease: number = 35; + audio_increase: number = 75; + audio_decrease: number = 25; // multipliers - public treble_multiplier: number = 0.5; - public mids_multiplier: number = 0.75; - public bass_multiplier: number = 1.8; + treble_multiplier: number = 0.5; + mids_multiplier: number = 0.75; + bass_multiplier: number = 1.8; // ignore value leveling for "silent" data - public minimum_volume: number = 0.005; + minimum_volume: number = 0.005; // use low latency audio? - public low_latency: boolean = false; + low_latency: boolean = false; + // show debug rendering? + show_canvas: boolean = true; } +/** +* Processed audio data representation +* @public +*/ +export interface WEAudio { + time: number; + ellapsed: number; + data: Float64Array; + bass: number; + mids: number; + highs: number; + sum: number; + min: number; + max: number; + average: number; + range: number; + silent: number; + intensity: number; +} + +/** +* @public +*/ const SettIDs = { equalize: 0, mono_audio: 1, @@ -61,6 +87,9 @@ const SettIDs = { minimum_volume: 10, }; +/** +* @public +*/ const PropIDs = { bass: 0, mids: 1, @@ -87,21 +116,30 @@ const PropIDs = { * - Wallpaper Engine Web Wallpaper environment *
      * - audio-processing supported web wallpaper +* @public * @extends {CComponent} */ export class WEAS extends CComponent { - // last processed audio object - public lastAudio = null; + /** @public last processed audio object */ + public lastAudio: WEAudio = null; - // settings object + /** @public settings object */ public settings: WEASettings = new WEASettings(); - // create transfer buffer - private inBuff = new Float64Array(DAT_LEN); + /** @public transfer buffer (last raw data) */ + public inBuff = new Float64Array(DAT_LEN); // web assembly functions private weasModule: WascInterface = null; + // debug render elements + private mainElm: HTMLDivElement; + private canvas1: HTMLCanvasElement; + private context1: CanvasRenderingContext2D; + private canvas2: HTMLCanvasElement; + private context2: CanvasRenderingContext2D; + private display: HTMLElement; + /** * delay audio initialization until page ready */ @@ -116,18 +154,28 @@ export class WEAS extends CComponent { * @ignore */ private async realInit() { - // if wallpaper engine context given, listen + // only listen if wallpaper engine context given if (!window['wallpaperRegisterAudioListener']) { Smallog.info('\'window.wallpaperRegisterAudioListener\' not given!'); return; } - this.weasModule = await wascWorker('WEAS.wasm', {}, !this.settings.low_latency); - const {run} = this.weasModule; + this.injectCSS(); + this.injectHTML(); + + this.weasModule = await wascWorker('WEAS.wasm', 4096, {}, !this.settings.low_latency); + + // assert + if (this.weasModule) Smallog.debug('Got Audio provider!'); + else { + Smallog.error('Could not create WebAssembly Audio provider! [Null-Object]'); + return; + } // pass settings to module await this.updateSettings(); + const {run} = this.weasModule; const self = this; // register audio callback on module @@ -140,11 +188,11 @@ export class WEAS extends CComponent { } // check nulls let consecutiveNull = 0; - for (let i = 0; i < 15; i++) { - const aA = audioArray[Math.floor(Math.random() * audioArray.length)]; + for (let i = 1; i < 18; i++) { + const aA = audioArray[i * 2]; if (aA == 0.0) consecutiveNull++; else consecutiveNull = 0; - if (consecutiveNull > 10) { + if (consecutiveNull > 16) { Smallog.debug('Skipping received Null data!: ' + JSON.stringify(audioArray)); return; } @@ -177,18 +225,77 @@ export class WEAS extends CComponent { .then((result) => { const {data, props} = result; const realProps = self.getProps(props); + const teim = performance.now() - start; // apply actual last Data from worker self.lastAudio = { - time: start / 1000, + time: start, + ellapsed: teim, data, ...realProps, - }; - // print info - Smallog.debug('Got Data from Worker! Time= ' + (performance.now() - start) + ', Data= ' + JSON.stringify(realProps)); + } as any; + // print info + // Smallog.debug('Got Data from Worker! Time= ' + teim + ', Data= ' + JSON.stringify(realProps)); }); }); } + /** + * Inject preview CSS + * @ignore + */ + private injectCSS() { + const st = document.createElement('style'); + st.innerHTML = ` + #weas-preview { + opacity: 0; + position: absolute; + z-index: 2; + background: #000000aa; + } + #weas-preview canvas { + background: none; + border: white 2px dotted; + } + #weas-preview.show { + opacity: 1; + } + `; + document.head.append(st); + } + + /** + * Inject preview canvas + * @ignore + */ + private injectHTML() { + this.mainElm = document.createElement('div'); + this.mainElm.id = 'weas-preview'; + this.mainElm.innerHTML = ` +
      + Raw Data: +
      + Left + Right + + Processed: +
      + +
      + + `; + document.body.append(this.mainElm); + + this.canvas1 = (document.getElementById('WEASCanvas1') as HTMLCanvasElement); + this.canvas1.width = window.innerWidth; + this.context1 = this.canvas1.getContext('2d'); + + this.canvas2 = (document.getElementById('WEASCanvas2') as HTMLCanvasElement); + this.canvas2.width = window.innerWidth; + this.context2 = this.canvas2.getContext('2d'); + + this.display = document.getElementById('WEASDisplay'); + } + /** * converts calculated output property number-array to string-associative-array * @param {ArrayLike} dProps processed properties @@ -209,10 +316,21 @@ export class WEAS extends CComponent { * !! CAVEAT: only available after init and module load !! *
      * Will send the processing settings to the WebAssembly module + * @public * @return {Promise} finished event */ public updateSettings(): Promise { if (!this.weasModule) return; + + // show or hide debug info + if (this.settings.debugging) { + if (!this.mainElm.classList.contains('show')) { + this.mainElm.classList.add('show'); + } + } else { + this.mainElm.classList.remove('show'); + } + const {run} = this.weasModule; return new Promise((resolve) => { @@ -235,7 +353,7 @@ export class WEAS extends CComponent { }) // Back to main context .then(() => { - Smallog.info('Sent Settings to WEAS: ' + JSON.stringify(sett)); + Smallog.debug('Sent Settings to WEAS: ' + JSON.stringify(sett)); resolve(); }); }); @@ -251,10 +369,51 @@ export class WEAS extends CComponent { * - the data is silent *
      * - data is too old (> 3s) + * @public */ public hasAudio() { + if (this.settings.show_canvas) this.updateCanvas(); + return this.settings.audioprocessing && this.lastAudio && this.lastAudio.silent == 0 && - (performance.now() / 1000 - this.lastAudio.time < 3); + (performance.now() - this.lastAudio.time < 3000); + } + + /** + * Update the debug canvas + */ + private updateCanvas() { + // update "raw" canvas + + // clear the intersection + this.context1.clearRect(0, 0, this.canvas1.width, this.canvas1.height); + this.context1.fillStyle = 'rgb(255,0,0)'; + const barWidth = Math.round(1.0 / this.inBuff.length * this.canvas1.width); + const halfCount = this.inBuff.length / 2; + for (let i = 0; i < halfCount; ++i) { + const height = this.canvas1.height * this.inBuff[i] / 2; + this.context1.fillRect(barWidth * i, this.canvas1.height - height, barWidth, height); + } + this.context1.fillStyle = 'rgb(0,0,255)'; + for (let i = halfCount; i < this.inBuff.length; ++i) { + const height = this.canvas1.height * this.inBuff[191 - i] / 2; + this.context1.fillRect(barWidth * i, this.canvas1.height - height, barWidth, height); + } + + // update "processed" data + const tmpData = Object.assign({}, this.lastAudio); + tmpData.data = null; + this.display.innerText = JSON.stringify(tmpData, null, '\t'); + + // update "processed" canvas + this.context2.clearRect(0, 0, this.canvas2.width, this.canvas2.height); + + if (this.lastAudio && this.lastAudio.data) { + this.context2.fillStyle = 'rgb(0,255,0)'; + for (let index = 0; index < this.lastAudio.data.length; index++) { + const height = this.canvas1.height * this.lastAudio.data[index] / 2; + this.context2.fillRect(barWidth * index, this.canvas1.height - height, barWidth, height); + } + } } } diff --git a/src/WEAS.wasm.asc b/src/WEAS.wasm.asc index 7a1bcac..7597968 100644 --- a/src/WEAS.wasm.asc +++ b/src/WEAS.wasm.asc @@ -21,6 +21,7 @@ declare function logf(value: f64): void; @external("env", "logi") declare function logi(value: u32): void; +/* @external("env", "logU32Array") declare function logU32Array(arr: Uint32Array): void; @@ -34,6 +35,7 @@ export function allocF64Array(length: i32): Float64Array { export function allocU32Array(length: i32): Uint32Array { return new Uint32Array(length); } +*/ @inline function deallocArray(arr: T[]): void { @@ -49,15 +51,15 @@ const DAT_LEN: i32 = 128; const HLF_LEN: i32 = 64; const QRT_LEN: i32 = 32; -const pNoise = [1.1760367470305, 0.85207379418243, 0.68842437227852, 0.63767902570829, +const pNoise = [0.97609734030594, 0.85207379418243, 0.68842437227852, 0.63767902570829, 0.5452348949654, 0.50723325864167, 0.4677726234682, 0.44204182748767, 0.41956517802157, 0.41517375040002, 0.41312118577934, 0.40618363960446, 0.39913707474975, 0.38207008614508, 0.38329789106488, 0.37472136606245, 0.36586428412968, 0.37603017335105, 0.39762590761573, 0.39391828858591, 0.37930603769622, 0.39433365764563, 0.38511504613859, 0.39082579241834, 0.3811852720504, 0.40231453727161, 0.40244151133175, 0.39965366884521, 0.39761103827545, 0.51136400422212, 0.66151212038954, 0.66312205226679, 0.7416276690995, 0.74614971301133, - 0.84797007577483, 0.8573583910469, 0.96382997811663, 0.99819377577185, 1.0628692615814, - 1.1059083969751, 1.1819808497335, 1.257092297208, 1.3226521464753, 1.3735992532905, + 0.84797007577483, 0.8573583910469, 0.96382997811663, 1.09819377577185, 1.1628692615814, + 1.2059083969751, 1.2819808497335, 1.357092297208, 1.3726521464753, 1.3735992532905, 1.4953223705889, 1.5310064942373, 1.6193923584808, 1.7094805527135, 1.7706604552218, 1.8491987941428, 1.9238418849406, 2.0141596921333, 2.0786429508827, 2.1575522518646, 2.2196355526005, 2.2660112509705, 2.320762171749, 2.3574848254513, 2.3986127976537, @@ -134,6 +136,30 @@ function invertAll(data: Float64Array): void { } } +// function for processing actual change +function equalizePeaks(array: Float64Array): void { + + smoothArray(peakData, 2); + smoothArray(minData, 3); + + for (i = 0; i < DAT_LEN; i++) { + // constant but slow regression + peakData[i] *= 0.96; + // peak now? + if(array[i] > peakData[i]) + peakData[i] = array[i] * 0.9; + // min calc + minData[i] *= 0.5; + // min now? + if(array[i] < minData[i]) + minData[i] = array[i] * 0.1; + else + minData[i] += (array[i] - minData[i]) / 10; + // scale equalize + array[i] = (array[i] + (array[i] - minData[i]) / (peakData[i] - minData[i])) / 2; + } +} + // filter peaks for full range function peakFilter(array: Float64Array, amount: f64): void { oldMax = 0; @@ -145,7 +171,7 @@ function peakFilter(array: Float64Array, amount: f64): void { if (tempArr[i] > newMax) newMax = tempArr[i]; } // re-scale & apply - tmpF = newMax / oldMax; + tmpF = newMax / oldMax / 1.25; for (i = 0; i < DAT_LEN; i++) array[i] = tempArr[i] / tmpF; } @@ -186,6 +212,10 @@ export const inputData = new Float64Array(DAT_LEN); // working data const workData = new Float64Array(DAT_LEN); +// equalize helper +const peakData = new Float64Array(DAT_LEN); +const minData = new Float64Array(DAT_LEN); + // PROCESSED AUDIO DATA // either: B-H | H-B | HL-BL-BR-HR | BL-HL-HR-BR // where ( B=bass, H=high, L=left, R=right ) @@ -205,7 +235,7 @@ export function update(): void { // so the input could be updated in the meantime workData.set(inputData); - // fix pink noise? + // equalize 1st step if (isOn(audioSettings[0])) correctPinkNoise(workData); @@ -213,24 +243,29 @@ export function update(): void { if (isOn(audioSettings[1])) stereoToMono(workData); + // highs in front/center if (isOn(audioSettings[2])) { - // flipped high & low mapping - if (isOn(audioSettings[1])) - // flip whole range + // flip mono range + if (isOn(audioSettings[1])) { invertAll(workData); - else { - // only flip first half of stereo - invertFirst(workData); - } + } + else { + invertSecond(workData); + } } + // bass in front/center else { - // normal high & low mapping - if (isOn(audioSettings[1])) { - // only flip the second half of the data - invertSecond(workData); + // flip stereo range + if (!isOn(audioSettings[1])) { + invertFirst(workData); } } + // equalize 2nd step + if (isOn(audioSettings[0])) { + equalizePeaks(workData); + } + // process peaks? if (isOn(audioSettings[3])) peakFilter(workData, audioSettings[3] + 1); @@ -244,7 +279,7 @@ export function update(): void { audioSettings[5], audioSettings[6]); - // process current frequency data and previous + // process current frequency data sum = 0; max = 0; bass = 0; @@ -259,7 +294,7 @@ export function update(): void { if (tmpF < min) min = tmpF; if (tmpF > max) max = tmpF; // process ranges - if (indx < (42 / 3)) bass += tmpF * audioSettings[9]; + if (indx < (42 / 2)) bass += tmpF * audioSettings[9]; else if (indx > 69) peaks += tmpF * audioSettings[7]; else mids += tmpF * audioSettings[8]; // calc peak average @@ -269,7 +304,7 @@ export function update(): void { // calc average with previous entry average = sum / (DAT_LEN as f64); silent = (max < audioSettings[10] / 1000) ? 1 : 0; - intensity = (bass * 8 - mids + peaks) / 6 / (average + 0.01); + intensity = (bass * 8 + mids + peaks) / 8 / (average + 0.01); range = max - min; // Apply Data diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 4327ccc..068294c 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -13,14 +13,18 @@ import {waitReady} from './Util'; import {Smallog} from './Smallog'; import {WEAS} from './WEAS'; +const IMG_SRC = './img/icue.png'; + const ClassName: string = '[WEICUE] '; const canvasX: number = 23; const canvasY: number = 7; const WaitTime: number = 30; const Transition: number = 3; + /** * iCUE processing settings +* @public * @extends {CSettings} */ export class CUESettings extends CSettings { @@ -31,7 +35,6 @@ export class CUESettings extends CSettings { public icue_area_height: number = 30; public icue_area_blur: number = 5; public icue_area_decay: number = 15; - public icue_area_preview: boolean = false; public icue_main_color: string = '0 0.8 0'; // AudiOrbits bg Color; used as "decay"-color aswell public main_color: string = '0 0 0'; @@ -44,6 +47,7 @@ export class CUESettings extends CSettings { *
      * Uses several different methods to create * Lighting effects for Corsair ICUE devices. +* @public * @extends {CComponent} */ export class WEICUE extends CComponent { @@ -58,6 +62,9 @@ export class WEICUE extends CComponent { private icueDevices = []; private icueInterval = null; + // preview time out + private prevTimeout = null; + // runtime values public settings: CUESettings = new CUESettings(); public isAvailable: boolean = false; @@ -74,10 +81,12 @@ export class WEICUE extends CComponent { // Plugin handler window['wallpaperPluginListener'] = { - onPluginLoaded: function(name, version) { - Smallog.info('Plugin: ' + name + ', Version: ' + version + ' : registered.', ClassName); - if (name === 'cue') this.isAvailable = true; - if (name === 'led') this.isAvailable = true; + onPluginLoaded: (name: string, version: string) => { + const lower = name.toLocaleLowerCase(); + if (lower === 'cue' || lower === 'led') { + this.isAvailable = true; + Smallog.debug(`Plugin loaded: ${name}, v${version}`, ClassName); + } }, }; waitReady().then(() => { @@ -127,8 +136,13 @@ export class WEICUE extends CComponent { text-shadow: 0 0 20px rgba(255, 255, 255, .5), 0 0 15px rgba(255, 255, 255, .5); } .cuePreview { + opacity: 0; position: absolute; - background: rgba(255, 0, 0, .3); + background: rgba(255, 0, 0, .1); + transition: all 2s; + } + .cuePreview.show { + opacity: 0.5; } `; document.head.append(st); @@ -145,178 +159,19 @@ export class WEICUE extends CComponent { // create icon (no ref needed) const imgg = document.createElement('img'); imgg.id = 'icuelogo'; - // @TODO remove this abomination - imgg.setAttribute('src', ` - data:image/png;base64,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 - AAAASUVORK5CYII= - `); + imgg.setAttribute('src', IMG_SRC); // make text holder this.texter = document.createElement('div'); this.texter.id = 'icuetext'; + + // preview area + this.preview = document.createElement('div'); + this.preview.classList.add('cuePreview'); + this.preview.innerHTML = '

      This is the LED/iCUE Projection-Area preview.
      It will hide automatically.

      You can disable LED/iCUE projection under:
      "Settings > 💡 LED/iCUE > Projection-mode = None"

      '; + // append image and text this.holder.append(imgg, this.texter); - document.body.append(this.holder); + document.body.append(this.holder, this.preview); } /** @@ -440,22 +295,22 @@ export class WEICUE extends CComponent { /** * show or hide preview + * @public * @return {Promise} finished */ public updateSettings(): Promise { - const sett = this.settings; - // create preview? - if (!this.preview && sett.icue_area_preview) { - this.preview = document.createElement('div'); - this.preview.classList.add('cuePreview'); - document.body.appendChild(this.preview); + // reset timeout? + if (this.prevTimeout) { + clearTimeout(this.prevTimeout); + this.prevTimeout = null; } - // update settings or destroy - if (this.preview) { - if (!sett.icue_area_preview) { - document.body.removeChild(this.preview); - this.preview = null; - } else Object.assign(this.preview.style, this.getArea(true)); + // update / show preview + if (this.isAvailable && this.preview && this.settings.icue_mode == 1) { + Object.assign(this.preview.style, this.getArea(true)); + this.preview.classList.add('show'); + this.prevTimeout = setTimeout(() => { + this.preview.classList.remove('show'); + }, 6000); } return Promise.resolve(); } @@ -468,7 +323,7 @@ export class WEICUE extends CComponent { private initCUE(count) { // wait for plugins if (!this.isAvailable) { - if (count < 100) setTimeout(() => this.initCUE(++count), 300); + if (count < 100) setTimeout(() => this.initCUE(++count), 150); else this.icueMessage('LED: Plugin not found!'); return; } @@ -534,6 +389,7 @@ export class WEICUE extends CComponent { /** * copy main canvas portion to our helper + * @public * @param {HTMLCanvasElementq} mainCanvas */ public updateCanvas(mainCanvas: HTMLCanvasElement) { diff --git a/src/WEWA.ts b/src/WEWA.ts index ceeb98c..66ddb59 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -9,9 +9,9 @@ import {waitReady} from './Util'; import {Smallog} from './Smallog'; -import {OfflineHelper} from './offline/OfflineHelper'; +import {register, reset} from './offline/OfflineHelper'; import {CC} from 'cookieconsent'; -import {myFetch} from './wasc-worker/WascRT'; +import {myFetch} from './wasc-worker/WascUtil'; const LogHead = '[WEWWA] '; const DefLang = 'de-de'; @@ -70,6 +70,8 @@ const DefLang = 'de-de'; * - cf longer cache policy (2d?) * - { + register(document.title.replace(' ', '')).then(() => { // continue initializing finished(); this.init(); @@ -480,19 +482,19 @@ export class WEWWA { const preFoot = ce('div'); preFoot.innerHTML = '
      '; - const reset = ce('a'); - reset.classList.add('red'); - reset.innerHTML = 'Reset ↩️'; - reset.addEventListener('click', (e) => { + const rst = ce('a'); + rst.classList.add('red'); + rst.innerHTML = 'Reset ↩️'; + rst.addEventListener('click', (e) => { if (!window.confirm('This action will clear ALL local data!\r\n\r\nAre you sure?')) { return; } - OfflineHelper.reset().then(() => { + reset().then(() => { localStorage.clear(); location = location; }); }); - preFoot.append(reset); + preFoot.append(rst); // footer with ident const footer = ce('div'); @@ -819,6 +821,7 @@ export class WEWWA { /** * Callback for UI-Settings changes * Will apply them to the storage and running wallaper. + * @public */ public setProperty(prop, elm) { // get the type and apply the value @@ -869,11 +872,12 @@ export class WEWWA { } } - // eslint-disable-next-line valid-jsdoc /** * will load the given file and return it as dataURL. * this way we can easily store whole files in the configuration & localStorage. * its not safe that this works with something else than image files. + * @param {string} url + * @param {function (data: (string | ArrayBuffer)): void} resCall * @ignore */ private loadXHRSaveLocal(url, resCall) { @@ -889,6 +893,7 @@ export class WEWWA { /** * Show or hide menu items based on eval condition + * @public */ public evaluateSettings() { const wewwaProps = this.project.general.properties; @@ -959,6 +964,7 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** * Send one or more properties to the Wallpaper + * @public */ public applyProp(prop) { const wpl = window['wallpaperPropertyListener']; @@ -970,6 +976,7 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** * Send paused-status to the Wallpaper + * @public */ public setPaused(val: boolean) { const wpl = window['wallpaperPropertyListener']; @@ -1021,7 +1028,7 @@ export class WEWWA { this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); this.source = this.ctx.createMediaStreamSource(stream); this.analyser = this.ctx.createAnalyser(); - this.analyser.smoothingTimeConstant = 0.35; + this.analyser.smoothingTimeConstant = 0.15; this.analyser.fftSize = 256; this.source.connect(this.analyser); @@ -1048,7 +1055,7 @@ export class WEWWA { let sIdx = 0; for (let i = 0; i < 64; i++) { stereo[i] = data[sIdx++] / 255; - stereo[127 - i] = data[sIdx++] / 255; + stereo[64 + i] = data[sIdx++] / 255; } return stereo; } @@ -1070,13 +1077,13 @@ export class WEWWA { // insert before marker const markr = document.getElementById('audioMarker'); - markr.parentElement.insertBefore(this.audio, markr); + markr.prepend(this.audio); this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); this.source = this.ctx.createMediaElementSource(this.audio); this.analyser = this.ctx.createAnalyser(); - this.analyser.smoothingTimeConstant = 0.35; - this.analyser.fftSize = 256; + this.analyser.smoothingTimeConstant = 0.1; + this.analyser.fftSize = 512; this.source.connect(this.ctx.destination); this.source.connect(this.analyser); @@ -1106,6 +1113,7 @@ export class WEWWA { /** * Stop the processing loop + * @public */ public stopAudioInterval() { window['persistAudioStream'] = null; diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 5d54c9b..df3878f 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -5,13 +5,6 @@ * Copyright (c) 2021 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. -* -* @description -* Displays a seizure warning image centered on html for a given Time. -* -* @todo -* - add trigger warn languages to project json -* - add trigger warn as html */ import {CComponent} from './CComponent'; @@ -19,30 +12,32 @@ import {CSettings} from './CSettings'; import {waitReady} from './Util'; const ELM_ID = 'triggerwarn'; +const IMG_SRC = './img/triggerwarning.png'; /** -* @TODO test getting text +* Seizure display warnings +* @public * @extends {CSettings} */ class WarnSettings extends CSettings { seizure_warning: boolean = true; - seizure_text: string = '/* */'; - animate_seconds: number = 1; - wait_seconds: number = 10; + animate_seconds: number = 2; + wait_seconds: number = 6; } /** -* Seizure warning display +* Displays a seizure warning image centered on html for a given Time. +* @public * @extends {CComponent} */ export class WarnHelper extends CComponent { + /* + * @public + */ public settings: WarnSettings = new WarnSettings(); private element: HTMLDivElement; - // promise behind showing the warning - private showResolve: any; - /** * Create and prepare once document ready */ @@ -63,11 +58,11 @@ export class WarnHelper extends CComponent { const st = document.createElement('style'); st.innerHTML = ` #${ELM_ID} { - object-fit: contain; - text-align: center; - max-height: 30vmax; - top: 25vmin; opacity: 0; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); transition: opacity ${this.settings.animate_seconds}s ease; } #${ELM_ID}.show { @@ -82,21 +77,15 @@ export class WarnHelper extends CComponent { * @ignore */ private injectHTML() { - this.element = document.createElement('div'); + this.element = document.createElement('img'); this.element.id = ELM_ID; + this.element.setAttribute('src', IMG_SRC); document.body.append(this.element); } - /** - * Set html text value - * @ignore - */ - private setText() { - this.element.innerHTML = this.settings.seizure_text.replace('\r\n', '
      '); - } - /** * Show the warning + * @public * @return {Promise} hidden again */ public show(): Promise { @@ -106,19 +95,20 @@ export class WarnHelper extends CComponent { resolve(); return; } - // wait for resolve by "Hide()" - this.showResolve = resolve; - // make text - this.setText(); // show it this.element.classList.add('show'); // wait some time - setTimeout(this.hide, this.settings.wait_seconds * 1000); + setTimeout(() => { + this.hide().then(() => { + resolve(); + }); + }, this.settings.wait_seconds * 1000); }); } /** * Hide warning + * @public * @return {Promise} hidden */ public hide(): Promise { @@ -126,8 +116,6 @@ export class WarnHelper extends CComponent { // hide it & wait this.element.classList.remove('show'); setTimeout(() => { - if (this.showResolve) this.showResolve(); - this.showResolve = null; resolve(); }, this.settings.animate_seconds * 1000); }); @@ -135,16 +123,15 @@ export class WarnHelper extends CComponent { /** * Settings have been changed + * @public * @return {Promise} finished */ public updateSettings(): Promise { - // update text - this.setText(); // fix for instantly removing the warning while it shows if (!this.settings.seizure_warning && this.element.classList.contains('show')) { return this.hide(); } // whatever - return; + return Promise.resolve(); } }; diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index afb9866..24dd842 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -13,9 +13,6 @@ import {Smallog} from '../Smallog'; // this should pack the serviceworker like a webwoker. import OfflineWorker from 'worker-loader!./Offline'; -const LogHead = '[OfflineHelper] '; -const LogErrS = 'service-worker is not supported.'; - /** * @ignore * Helper class for loading and registering the ServiceWorker @@ -29,53 +26,62 @@ const LogErrS = 'service-worker is not supported.'; * the ServiceWorker will cache these files and automatically load them if the website is ran offline. *
      * ServiceWorker is a progressive technology. Some browsers will be unsupported... +* @public */ -export module OfflineHelper { - // function helper, so OfflineWorker is actually processed - // eslint-disable-next-line require-jsdoc - function DontRemove() { - return new OfflineWorker(); - }; - // In order to intercept ALL fetch-requests offline, the scope "/" (root) is required. - // when you put in in a sub-directory like "/js/", the scope is also "/js/". - // Then, your HTTP Server will have to send the REPONSE HEADER `service-worker-allowed: /` - // otherwise it will cause an ERROR in your Browser. So: Putting the ServiceWorker in root folder is easiest. - // obviously with webpack, this causes a problem, when you are not outputting directly into the root dir... - // eslint-disable-next-line require-jsdoc - export function register(name: string, worker = 'Offline.worker.js', oFile = 'offlinefiles.json') { - return new Promise(async (resolve) => { - if ('serviceWorker' in navigator) { - const workerPath = `${worker}?name=${name}&jsonPath=${oFile}`; - await navigator.serviceWorker.register(workerPath, {scope: '/'}) - .then(() => Smallog.info('service-worker registration complete.', LogHead), - () => Smallog.error('service-worker registration failure.', LogHead)) - .then(() => resolve(true)); - return true; - } else { - Smallog.error(LogErrS, LogHead); - resolve(false); - } - }); - } +// function helper, so OfflineWorker is actually processed +// eslint-disable-next-line require-jsdoc +function DontRemove() { + return new OfflineWorker(); +}; + +/** +* In order to intercept ALL fetch-requests offline, the scope "/" (root) is required. +* when you put in in a sub-directory like "/js/", the scope is also "/js/". +* Then, your HTTP Server will have to send the REPONSE HEADER `service-worker-allowed: /` +* otherwise it will cause an ERROR in your Browser. So: Putting the ServiceWorker in root folder is easiest. +* obviously with webpack, this causes a problem, when you are not outputting directly into the root dir... +* eslint-disable-next-line require-jsdoc +* +* @public +* @param {string} name +* @param {string} worker +* @param {string} oFile +* @return {Promise} +*/ +export function register(name: string, worker = 'Offline.worker.js', oFile = 'offlinefiles.json') { + return new Promise(async (resolve) => { + if ('serviceWorker' in navigator) { + const workerPath = `${worker}?name=${name}&jsonPath=${oFile}`; + await navigator.serviceWorker.register(workerPath, {scope: '/'}) + .then(() => Smallog.info('service-worker registration complete.', '[OfflineHelper] '), + () => Smallog.error('service-worker registration failure.', '[OfflineHelper] ')) + .then(() => resolve(true)); + return true; + } else { + Smallog.error('not supported!', '[OfflineHelper] '); + resolve(false); + } + }); +} - /** - * unregister all service workers - * @return {Promise} finished - */ - export async function reset() { - return new Promise((resolve) => { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.getRegistrations().then(async (registrations) => { - for (const registration of registrations) { - await registration.unregister(); - } - resolve(true); - }); - } else { - Smallog.error(LogErrS, LogHead); - resolve(false); - } - }); - } +/** +* unregister all service workers +* @return {Promise} finished +* @public +*/ +export async function reset() { + return new Promise((resolve) => { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.getRegistrations().then(async (registrations) => { + for (const registration of registrations) { + await registration.unregister(); + } + resolve(true); + }); + } else { + Smallog.error('not supported!', '[OfflineHelper] '); + resolve(false); + } + }); } diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts new file mode 100644 index 0000000..3575cf1 --- /dev/null +++ b/src/three/EffectComposer.ts @@ -0,0 +1,303 @@ +/** +* @author alteredq / http://alteredqualia.com/ +* +* @author hexxone / https://hexx.one +*/ + +import {LinearFilter, PerspectiveCamera, Quaternion, RGBAFormat, Scene, Vector2, WebGLRenderer, WebGLRenderTarget, XRFrame} from 'three'; +import {BasePass} from './pass/BasePass'; +import {RenderPass} from './pass/RenderPass'; +import {ShaderPass} from './pass/ShaderPass'; + +const defaultParams = { + minFilter: LinearFilter, + magFilter: LinearFilter, + format: RGBAFormat, + stencilBuffer: false, +}; + +/** +* render shader chain +* @public +*/ +export class EffectComposer { + private scene: Scene; + private camera: PerspectiveCamera; + private renderer: WebGLRenderer; + + private varCam = new PerspectiveCamera(); + + private viewSize: Vector2; + + private globalPrecision: string; + + private _previousFrameTime = Date.now(); + + private defaultTarget: WebGLRenderTarget; + + private renderWrite: WebGLRenderTarget; + private writeBuffer: WebGLRenderTarget; + + private renderRead: WebGLRenderTarget; + private readBuffer: WebGLRenderTarget; + + private normPass: RenderPass; + private xrPass: RenderPass; + + // render by default + public enabled: boolean = true; + public passes: BasePass[] = []; + + + /** + * Instantiate + * @param {Scene} scene + * @param {PerspectiveCamera} camera + * @param {WebGLRenderer} renderer + * @param {string} globalPrec + * @param {WebGLRenderTarget} renderTarget + */ + constructor(scene: Scene, camera: PerspectiveCamera, renderer: WebGLRenderer, globalPrec: string = 'mediump', renderTarget?: WebGLRenderTarget) { + this.scene = scene; + this.camera = camera; + this.renderer = renderer; + this.viewSize = renderer.getSize(new Vector2()); + + this.varCam = new PerspectiveCamera(camera.fov, camera.aspect, camera.near, camera.far); + + // use a new default render target if none is given + this.defaultTarget = new WebGLRenderTarget(this.viewSize.width, this.viewSize.height, defaultParams); + this.defaultTarget.texture.name = 'EffectComposer.dt'; + + if (renderTarget === undefined) { + renderTarget = this.defaultTarget.clone(); + renderTarget.texture.name = 'EffectComposer.wt'; + } + + // set write buffer for shader pass rendering + this.renderWrite = renderTarget; + this.writeBuffer = this.renderWrite; + + // set input buffer for shader pass rendering + this.renderRead = renderTarget.clone(); + this.renderRead.texture.name = 'EffectComposer.rt'; + this.readBuffer = this.renderRead; + + this.passes = []; + this._previousFrameTime = Date.now(); + this.globalPrecision = globalPrec; + + this.normPass = new RenderPass(scene, camera, null, 0x000000, 1); + this.xrPass = new RenderPass(scene, this.varCam, null, 0x000000, 1); + } + + /** + * Append a shader to the chain + * @public + * @param {BasePass} pass Shader to add + */ + public addPass(pass: BasePass) { + const p = this.wrapPrecision(pass); + p.setSize(this.viewSize.width, this.viewSize.height); + this.passes.push(p); + } + + /** + * Insert a shader in the chain + * @public + * @param {BasePass} pass Shader to add + * @param {number} index position + */ + public insertPass(pass: BasePass, index: number) { + const p = this.wrapPrecision(pass); + p.setSize(this.viewSize.width, this.viewSize.height); + this.passes.splice(index, 0, p); + } + + /** + * Checks if the given shader should be rendererd to screen + * @param {number} passIndex position + * @return {boolean} + */ + private isLastEnabledPass(passIndex: number) { + for (let i = passIndex + 1; i < this.passes.length; i++) { + if (this.passes[i].enabled) return false; + } + return true; + } + + /** + * Render the shader-chain for 1 frame + * @public + * @param {number} deltaTime if not given, will calculate its own + * @param {XRFrame} frame Currently rendering XR frame? + */ + public render(deltaTime?: number, frame?: XRFrame) { + // deltaTime value is in seconds + const dn = performance.now(); + if (deltaTime === undefined) { + deltaTime = (dn - this._previousFrameTime) * 0.001; + } + this._previousFrameTime = dn; + const size = new Vector2(); + this.renderer.getSize( size ); + const currentRenderTarget = this.renderer.getRenderTarget(); + // has enabled passes? + const hasTargets = this.passes.filter((p) => p.enabled).length > 0; + + // clear surface ? + if ( this.renderer.autoClear ) this.renderer.clear(); + + // do spilt rendering + if (this.renderer.xr.isPresenting && frame !== null) { + this.scene.updateMatrixWorld(); + if ( this.camera.parent === null ) this.camera.updateMatrixWorld(); + + // update cameras + const pose = frame.getViewerPose(this.renderer.xr.getReferenceSpace()); + const views = pose.views; + const viewSize = size.width / views.length; + + // base position + const camPos = this.camera.position.clone(); + + // dont use native XR features now + this.renderer.xr.enabled = false; + this.renderer.setScissorTest( true ); + + // render + for (let i = 0; i < views.length; i++) { + const view = views[i]; + + // position + const varPos = view.transform.position; + this.varCam.position.set(camPos.x + varPos.x, + camPos.y + (varPos.y - 1.6), + camPos.z + varPos.z); + + // orientation + const vo = view.transform.orientation; + this.varCam.setRotationFromQuaternion(new Quaternion(vo.x, vo.y, vo.z, vo.w)); + + // matrix + this.varCam.projectionMatrix.fromArray(view.projectionMatrix); + this.varCam.near = this.camera.near; + + // render + const offX = viewSize * i; + this.renderer.setScissor( offX, 0, viewSize, size.height ); + this.renderer.setViewport( offX, 0, viewSize, size.height ); + + // pass buffers flipped to avoid swap + this.xrPass.render(this.renderer, this.readBuffer, this.writeBuffer, false, !hasTargets); + this.passes.forEach((pass, i) => { + if (!pass.enabled) return; + pass.setSize(viewSize, size.height); + pass.render(this.renderer, this.writeBuffer, this.readBuffer, false, this.isLastEnabledPass(i)); + if (pass.needsSwap) this.swapBuffers(); + }); + } + + // reset features + this.renderer.setScissorTest( false ); + this.renderer.xr.enabled = true; + } else { + // render default + this.camera.rotation.set(0, 0, 0); + this.renderer.setScissor( 0, 0, size.width, size.height ); + this.renderer.setViewport( 0, 0, size.width, size.height ); + // pass buffers flipped to avoid swap + this.normPass.render(this.renderer, this.readBuffer, this.writeBuffer, false, !hasTargets); + this.passes.forEach((pass, i) => { + if (!pass.enabled) return; + pass.setSize(size.width, size.height); + pass.render(this.renderer, this.writeBuffer, this.readBuffer, false, this.isLastEnabledPass(i)); + if (pass.needsSwap) this.swapBuffers(); + }); + } + + this.renderer.setRenderTarget(currentRenderTarget); + } + + /** + * Destroy the current shader-chain + * @public + * @param {WebGLRenderTarget} renderTarget target to Reset (optional) + */ + public reset(renderTarget?: WebGLRenderTarget) { + if (renderTarget === undefined) { + renderTarget = this.defaultTarget.clone(); + renderTarget.texture.name = 'EffectComposer.wt'; + } + + this.renderWrite.dispose(); + this.renderRead.dispose(); + + this.renderWrite = renderTarget; + this.writeBuffer = this.renderWrite; + + this.renderRead = renderTarget.clone(); + this.renderRead.texture.name = 'EffectComposer.rt'; + this.readBuffer = this.renderRead; + + this.passes = []; + + this.setSize(this.viewSize.width, this.viewSize.height); + } + + /** + * Updated buffer size + * @public + * @param {number} width X + * @param {number} height Y + */ + public setSize(width: number, height: number) { + this.renderWrite.setSize(width, height); + this.renderRead.setSize(width, height); + this.passes.forEach((pass) => pass.setSize(width, height)); + this.viewSize.set(width, height); + } + + /* UTILS */ + + /** + * Prefixes custom WebGL-precision to shaders + * + * @param {BasePass} pass Shader to Wrap + * @return {BasePass} + * @ignore + */ + private wrapPrecision(pass: BasePass): BasePass { + if (pass instanceof ShaderPass) { + const copy = pass as ShaderPass; + // get prefix + let pre = 'precision ' + this.globalPrecision + ' float;\r\n ' + + 'precision ' + this.globalPrecision + ' int;\r\n '; + // "medium" sampler precision should always be available for "high" float precision. + if (this.globalPrecision == 'highp') { + pre += 'precision mediump sampler2D;\r\n ' + + 'precision mediump samplerCube;\r\n '; + } + // apply it + if (copy.material.vertexShader) { + copy.material.vertexShader = pre + copy.material.vertexShader; + } + if (copy.material.fragmentShader) { + copy.material.fragmentShader = pre + copy.material.fragmentShader; + } + } + return pass; + } + + /** + * Some shaders write to Input rather than Output... + * + * This is a workaround to pass their data further down the render-chain + * @ignore + */ + private swapBuffers() { + const tmp = this.readBuffer; + this.readBuffer = this.writeBuffer; + this.writeBuffer = tmp; + } +} diff --git a/src/three/XRHelper.ts b/src/three/XRHelper.ts new file mode 100644 index 0000000..fc9dac1 --- /dev/null +++ b/src/three/XRHelper.ts @@ -0,0 +1,146 @@ +/** +* @author hexxone / https://hexx.one +* @author mrdoob / http://mrdoob.com +* @author Mugen87 / https://github.com/Mugen87 +*/ + +import {Navigator, XRSession} from 'three'; +import {CComponent} from '../CComponent'; +import {CSettings} from '../CSettings'; + +/** + * XR Settings + * @extends {CSettings} + */ +export class XRSettings extends CSettings { + xr_mode: boolean = false; +} + +/** +* XR / VR / AR Helper class. +* Provides availability information and starts/stops a three-js XR session +* @public +* @extends {CComponent} +*/ +export class XRHelper extends CComponent { + public settings: XRSettings = new XRSettings(); + + private nav: Navigator; + private button: HTMLButtonElement; + private currentSession: XRSession; + + /** + * Get typed navigator + */ + constructor() { + super(); + this.nav = navigator as Navigator; + this.createBtn(); + } + + /** + * Create the "Exit" Button + */ + private createBtn() { + const btn = this.button = document.createElement('button'); + btn.disabled = true; + btn.style.display = 'none'; + btn.style.position = 'absolute'; + btn.style.bottom = '10px'; + btn.style.padding = '12px 6px'; + btn.style.border = '1px solid #fff'; + btn.style.borderRadius = '4px'; + btn.style.background = 'rgba(0,0,0,0.1)'; + btn.style.color = '#fff'; + btn.style.font = 'normal 13px sans-serif'; + btn.style.textAlign = 'center'; + btn.style.opacity = '0.5'; + btn.style.outline = 'none'; + btn.style.zIndex = '99999'; + + btn.onmouseenter = () => { + btn.style.opacity = '1.0'; + }; + btn.onmouseleave = () => { + btn.style.opacity = '0.5'; + }; + + document.body.append(btn); + } + + /** + * @return {boolean} whether XR is supported and available or not + */ + private async isSupported() { + if ('xr' in this.nav) { + return (await this.nav.xr.isSessionSupported('immersive-vr')); + } + return false; + } + + /** + * Trys to start a Web-XR session. + * if successfull, will provide functionality for leaving web-XR again. + * @param {function (params:XRSession): void} sessionCallback + * @return {Promise} + */ + public async enableSession(sessionCallback: (xrs: XRSession) => void): Promise { + return new Promise(async (resolve) => { + // check availability + const avail = await this.isSupported(); + if (!avail) { + if (window.isSecureContext === false && confirm('WebXR may need HTTPS to function. Redirect?')) { + document.location.href = document.location.href.replace(/^http:/, 'https:'); + } else { + this.button.textContent = 'VR not available!'; + this.button.style.display = 'block'; + console.error('[WEBXR] Not avaiable! More info: https://immersiveweb.dev/'); + } + resolve(false); + return; + } + + this.button.textContent = 'Enter XR'; + this.button.style.display = 'block'; + this.button.disabled = false; + + // "Toggle" style event listener + this.button.addEventListener('click', async () => { + if (!avail) return; + // end previous session + if (this.currentSession) { + await this.currentSession.end(); + return; + } + + // WebXR's requestReferenceSpace only works if the corresponding feature + // was requested at session creation time. For simplicity, just ask for + // the interesting ones as optional features, but be aware that the + // requestReferenceSpace call will fail if it turns out to be unavailable. + // ('local' is always available for immersive sessions and doesn't need to + // be requested separately.) + const sessionInit = {optionalFeatures: ['local-floor']}; /* , 'bounded-floor'*/ + this.nav.xr.requestSession('immersive-vr', sessionInit).then((sess) => { + this.currentSession = sess; + + const lstnr = (/* event*/) => { + this.currentSession.removeEventListener('end', lstnr); + this.currentSession = null; + this.button.textContent = 'Enter VR'; + sessionCallback(null); + }; + sess.addEventListener('end', lstnr); + // show exit button + this.button.textContent = 'Exit VR'; + sessionCallback(sess); + }, + (reason) => { + console.error('[WEBXR] RequestSession failed! Reason: ' + reason); + }); + }); + + // success + resolve(true); + }); + } +}; diff --git a/src/three/index.ts b/src/three/index.ts new file mode 100644 index 0000000..5394cfa --- /dev/null +++ b/src/three/index.ts @@ -0,0 +1,26 @@ +/** +* @author alteredq / http://alteredqualia.com/ +* @author hexxone / https://hexx.one +* +* @ignore +*/ + +export * from './EffectComposer'; +export * from './XRHelper'; + +export * from './pass/BasePass'; +export * from './pass/FullScreenHelper'; +export * from './pass/RenderPass'; +export * from './pass/ShaderPass'; +export * from './pass/UnrealBloomPass'; + +export * from './shader/BaseShader'; +export * from './shader/BlendShader'; +export * from './shader/BlurShader'; +export * from './shader/ChromaticShader'; +export * from './shader/CopyShader'; +export * from './shader/FractalMirrorShader'; +export * from './shader/FXAAShader'; +export * from './shader/LuminosityHighPassShader'; +export * from './shader/LUTShader'; +export * from './shader/LUTShaderNearest'; diff --git a/src/three/pass/BasePass.ts b/src/three/pass/BasePass.ts new file mode 100644 index 0000000..167ebab --- /dev/null +++ b/src/three/pass/BasePass.ts @@ -0,0 +1,27 @@ +/** +* @author alteredq / http://alteredqualia.com/ +* @author hexxone / https://hexx.one +* +* Basic shader pass interface +* @public +*/ +export interface BasePass { + // child name + name: string; + + // if set to true, the pass is rendered in the chain. + // otherwise => ignored + enabled: boolean; // = true; + + // if set to true, the pass indicates to swap read and write buffer after rendering + needsSwap: boolean; // = true; + + // if set to true, the pass clears its buffer before rendering + clear: boolean; // = false; + + dispose(); + + setSize(width: number, height: number); + + render(renderer: THREE.WebGLRenderer, writeBuffer: THREE.WebGLRenderTarget, readBuffer: THREE.WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean); +} diff --git a/src/three/pass/FullScreenHelper.ts b/src/three/pass/FullScreenHelper.ts new file mode 100644 index 0000000..7fb8430 --- /dev/null +++ b/src/three/pass/FullScreenHelper.ts @@ -0,0 +1,56 @@ +/** +* @author alteredq / http://alteredqualia.com/ +* +* @author hexxone / https://hexx.one +*/ + +import {BufferGeometry, Camera, Material, Mesh, OrthographicCamera, PlaneBufferGeometry, WebGLRenderer} from 'three'; + +/** +* Helper for passes that need to fill the viewport with a single quad. +* used to render on a PlaneGeometry ("texture") +* @public +*/ +export class FullScreenHelper { + private _mat = null; + + public camera: Camera = null; + public geometry: BufferGeometry = null; + public mesh: Mesh = null; + + /** + * instantiate + * @param {Material} material + */ + constructor(material: Material) { + this._mat = material; + this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1); + this.geometry = new PlaneBufferGeometry(2, 2); + this.mesh = new Mesh(this.geometry, material); + } + + /** + * Change mesh material + * @param {Material} mat + */ + public setMaterial(mat: Material) { + this.mesh.material = mat; + } + + /** + * Render the 2D-environment + * @param {WebGLRenderer} renderer + */ + public render(renderer: WebGLRenderer) { + renderer.render(this.mesh, this.camera); + } + + /** + * Destroy 2D-environment + */ + public dispose() { + this.camera.clear(); + this.mesh.clear(); + this.geometry.dispose(); + } +} diff --git a/src/three/pass/RenderPass.ts b/src/three/pass/RenderPass.ts new file mode 100644 index 0000000..c1b7d57 --- /dev/null +++ b/src/three/pass/RenderPass.ts @@ -0,0 +1,101 @@ +/** +* @author alteredq / http://alteredqualia.com/ +* +* @author hexxone / https://hexx.one +*/ + +import {Camera, Color, Material, Scene} from 'three'; +import {BasePass} from './BasePass'; + +/** +* Shader Render Helper +* @public +*/ +export class RenderPass implements BasePass { + name = 'RenderPass'; + enabled = true; + needsSwap = true; + + clear = true; + + clearColor: Color = null; + clearAlpha: number = null; + clearDepth = false; + + private scene: Scene = null; + private camera: Camera = null; + private overMat: Material = null; + + /** + * Construct helper + * @param {Scene} scene + * @param {Camera} camera + * @param {Material} overMat + * @param {Color} clearColor + * @param {number} clearAlpha + */ + constructor(scene: Scene, camera: Camera, overMat: Material, clearColor, clearAlpha: number) { + this.scene = scene; + this.camera = camera; + + this.overMat = overMat; + + this.clearColor = clearColor; + this.clearAlpha = (clearAlpha !== undefined) ? clearAlpha : 0; + } + + /** + * Destroy shader + */ + public dispose() { + throw new Error('Method not implemented.'); + } + + /** + * Updated screen size + * @param {number} width X + * @param {number} height Y + */ + public setSize(width: number, height: number) { } + + /** + * Render Frame + * @param {WebGLRenderer} renderer Context + * @param {WebGLRenderTarget} writeBuffer Output + * @param {WebGLRenderTarget} readBuffer Input + * @param {boolean} maskActive filter + * @param {boolean} renderToScreen render to canvas OR buffer + * @param {Camera} camera (optional) + * @public + */ + public render(renderer: THREE.WebGLRenderer, writeBuffer: THREE.WebGLRenderTarget, readBuffer: THREE.WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean) { + const oldAutoClear = renderer.autoClear; + renderer.autoClear = false; + + this.scene.overrideMaterial = this.overMat; + + let oldClearColor: Color; + let oldClearAlpha: number; + + if (this.clearColor) { + renderer.getClearColor(oldClearColor); + oldClearAlpha = renderer.getClearAlpha(); + renderer.setClearColor(this.clearColor, this.clearAlpha); + } + + if (this.clearDepth) { + renderer.clearDepth(); + } + + renderer.setRenderTarget(renderToScreen ? null : writeBuffer); + + // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 + if (this.clear) renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); + renderer.render(this.scene, this.camera); + if (this.clearColor) renderer.setClearColor(oldClearColor, oldClearAlpha); + + this.scene.overrideMaterial = null; + renderer.autoClear = oldAutoClear; + } +} + diff --git a/src/three/pass/ShaderPass.ts b/src/three/pass/ShaderPass.ts new file mode 100644 index 0000000..791f335 --- /dev/null +++ b/src/three/pass/ShaderPass.ts @@ -0,0 +1,102 @@ +/** +* @author alteredq / http://alteredqualia.com/ +* +* @author hexxone / https://hexx.one +*/ + +import {ShaderMaterial, UniformsUtils, Vector2, WebGLRenderer, WebGLRenderTarget} from 'three'; +import {BaseShader} from '../shader/BaseShader'; + +import {FullScreenHelper} from './FullScreenHelper'; +import {BasePass} from './BasePass'; + +/** +* ThreeJS Pass for easy full screen shaders +* @public +*/ +export class ShaderPass implements BasePass { + name: string; + enabled = true; + needsSwap = true; + clear = false; + material: ShaderMaterial; + textureID: string; + uniforms: any; + fsQuad: FullScreenHelper; + iRes: Vector2; + + /** + * Make Pass + * default Material will enable transparency! + * @param {BaseShader|ShaderMaterial} shader Create From + * @param {string} textureID Input Uniform Texture name + */ + constructor(shader: BaseShader | ShaderMaterial, textureID: string = 'tDiffuse') { + this.textureID = textureID; + + if (shader instanceof ShaderMaterial) { + this.name = 'ShaderMaterial'; + this.uniforms = shader.uniforms; + this.material = shader; + } else if (shader) { + this.name = shader.shaderID; + this.uniforms = UniformsUtils.clone(shader.uniforms); + this.material = new ShaderMaterial({ + defines: Object.assign({}, shader.defines), + uniforms: this.uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader, + }); + } + this.material.transparent = true; + this.fsQuad = new FullScreenHelper(this.material); + } + + /** + * Destroy Pass + * @public + */ + public dispose() { + this.fsQuad.dispose(); + } + + /** + * Canvas size update + * @param {number} width X + * @param {number} height Y + * @public + */ + public setSize(width: number, height: number) { + this.iRes = new Vector2(width, height); + } + + /** + * Render frame with chaining-support + * @param {WebGLRenderer} renderer + * @param {WebGLRenderTarget} writeBuffer wB + * @param {WebGLRenderTarget} readBuffer rB + * @param {boolean} maskActive mA + * @param {boolean} renderToScreen render to canvas OR buffer + * @public + */ + public render(renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean) { + if (this.uniforms[this.textureID]) { + this.uniforms[this.textureID].value = readBuffer.texture; + } + + if (this.uniforms.iResolution) { + this.uniforms.iResolution.value = this.iRes; + } + + this.fsQuad.setMaterial(this.material); + + if (renderToScreen) { + renderer.setRenderTarget(null); + } else { + renderer.setRenderTarget(writeBuffer); + // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/js/pull/15571#issuecomment-465669600 + if (this.clear) renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); + } + this.fsQuad.render(renderer); + } +} diff --git a/src/three/pass/UnrealBloomPass.ts b/src/three/pass/UnrealBloomPass.ts new file mode 100644 index 0000000..efaec68 --- /dev/null +++ b/src/three/pass/UnrealBloomPass.ts @@ -0,0 +1,390 @@ +/** +* @author spidersharma / http://eduperiment.com/ +*/ + +import {AdditiveBlending, Color, LinearFilter, MeshBasicMaterial, RGBAFormat, ShaderMaterial, UniformsUtils, Vector2, Vector3, WebGLRenderer, WebGLRenderTarget} from 'three'; +import {FullScreenHelper} from './FullScreenHelper'; +import {BasePass} from './BasePass'; + +import {CopyShader} from '../shader/CopyShader'; +import {LuminosityHighPassShader} from '../shader/LuminosityHighPassShader'; + +/** +* Inspired from Unreal Engine +* https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/ +* +* @public +*/ +export class UnrealBloomPass implements BasePass { + name = 'UnrealBloom'; + strength = null; + radius = null; + resolution = null; + threshold = null; + renderTargetBright = null; + highPassUniforms = null; + + // create color only once here, reuse it later inside the render function + clear = true; + clearColor = new Color(0, 0, 0); + renderTargetsHorizontal: WebGLRenderTarget[] = []; + renderTargetsVertical: WebGLRenderTarget[] = []; + nMips = 5; + + separableBlurMaterials: ShaderMaterial[] = []; + materialHighPassFilter: ShaderMaterial = null; + compositeMaterial: ShaderMaterial = null; + materialCopy: ShaderMaterial = null; + bloomTintColors = null; + copyUniforms = null; + enabled = true; + needsSwap = false; + + oldClearColor = new Color(); + oldClearAlpha = 1; + + basic = new MeshBasicMaterial(); + fsQuad = new FullScreenHelper(null); + + BlurDirectionX = new Vector2(1.0, 0.0); + BlurDirectionY = new Vector2(0.0, 1.0); + + /** + * Construct bloom shader + * @param {Vector2} resolution size + * @param {number} strength multiplier + * @param {number} radius size + * @param {number} threshold min val + */ + constructor(resolution: Vector2, strength: number, radius: number, threshold: number) { + this.resolution = (resolution) ? resolution : new Vector2(256, 256); + this.strength = (strength !== undefined) ? strength : 1; + this.radius = radius; + this.threshold = threshold; + + + // render targets + const pars = {minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat}; + let resx = Math.round(this.resolution.x / 2); + let resy = Math.round(this.resolution.y / 2); + + this.renderTargetBright = new WebGLRenderTarget(resx, resy, pars); + this.renderTargetBright.texture.name = 'UnrealBloomPass.bright'; + this.renderTargetBright.texture.generateMipmaps = false; + + for (let i = 0; i < this.nMips; i++) { + const renderTargetHorizonal = new WebGLRenderTarget(resx, resy, pars); + renderTargetHorizonal.texture.name = 'UnrealBloomPass.h' + i; + renderTargetHorizonal.texture.generateMipmaps = false; + this.renderTargetsHorizontal.push(renderTargetHorizonal); + + const renderTargetVertical = new WebGLRenderTarget(resx, resy, pars); + renderTargetVertical.texture.name = 'UnrealBloomPass.v' + i; + renderTargetVertical.texture.generateMipmaps = false; + this.renderTargetsVertical.push(renderTargetVertical); + + resx = Math.round(resx / 2); + resy = Math.round(resy / 2); + } + + // luminosity high pass material + + const highPassShader = new LuminosityHighPassShader(); + this.highPassUniforms = UniformsUtils.clone(highPassShader.uniforms); + this.highPassUniforms['luminosityThreshold'].value = threshold; + this.highPassUniforms['smoothWidth'].value = 0.01; + + this.materialHighPassFilter = new ShaderMaterial({ + uniforms: this.highPassUniforms, + vertexShader: highPassShader.vertexShader, + fragmentShader: highPassShader.fragmentShader, + defines: {}, + }); + + // Gaussian Blur Materials + const kernelSizeArray = [3, 5, 7, 9, 11]; + resx = Math.round(this.resolution.x / 2); + resy = Math.round(this.resolution.y / 2); + + for (let i = 0; i < this.nMips; i++) { + this.separableBlurMaterials.push(this.getSeperableBlurMaterial(kernelSizeArray[i])); + this.separableBlurMaterials[i].uniforms['texSize'].value = new Vector2(resx, resy); + + resx = Math.round(resx / 2); + resy = Math.round(resy / 2); + } + + // Composite material + this.compositeMaterial = this.getCompositeMaterial(this.nMips); + this.compositeMaterial.uniforms['blurTexture1'].value = this.renderTargetsVertical[0].texture; + this.compositeMaterial.uniforms['blurTexture2'].value = this.renderTargetsVertical[1].texture; + this.compositeMaterial.uniforms['blurTexture3'].value = this.renderTargetsVertical[2].texture; + this.compositeMaterial.uniforms['blurTexture4'].value = this.renderTargetsVertical[3].texture; + this.compositeMaterial.uniforms['blurTexture5'].value = this.renderTargetsVertical[4].texture; + this.compositeMaterial.uniforms['bloomStrength'].value = strength; + this.compositeMaterial.uniforms['bloomRadius'].value = 0.1; + this.compositeMaterial.needsUpdate = true; + + const bloomFactors = [1.0, 0.8, 0.6, 0.4, 0.2]; + this.compositeMaterial.uniforms['bloomFactors'].value = bloomFactors; + this.bloomTintColors = [new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1)]; + this.compositeMaterial.uniforms['bloomTintColors'].value = this.bloomTintColors; + + // copy material + + const copyShader = new CopyShader(); + + this.copyUniforms = UniformsUtils.clone(copyShader.uniforms); + this.copyUniforms['opacity'].value = 1.0; + + this.materialCopy = new ShaderMaterial({ + uniforms: this.copyUniforms, + vertexShader: copyShader.vertexShader, + fragmentShader: copyShader.fragmentShader, + blending: AdditiveBlending, + depthTest: false, + depthWrite: false, + transparent: true, + }); + } + + /** + * Destroy shader + */ + public dispose() { + for (let i = 0; i < this.renderTargetsHorizontal.length; i++) { + this.renderTargetsHorizontal[i].dispose(); + } + for (let i = 0; i < this.renderTargetsVertical.length; i++) { + this.renderTargetsVertical[i].dispose(); + } + this.renderTargetBright.dispose(); + this.fsQuad.dispose(); + } + + /** + * Updated screen size + * @param {number} width X + * @param {number} height Y + */ + public setSize(width: number, height: number) { + let resx = Math.round(width / 2); + let resy = Math.round(height / 2); + this.renderTargetBright.setSize(resx, resy); + + for (let i = 0; i < this.nMips; i++) { + this.renderTargetsHorizontal[i].setSize(resx, resy); + this.renderTargetsVertical[i].setSize(resx, resy); + + this.separableBlurMaterials[i].uniforms['texSize'].value = new Vector2(resx, resy); + + resx = Math.round(resx / 2); + resy = Math.round(resy / 2); + } + } + + /** + * Render Frame + * @param {WebGLRenderer} renderer Context + * @param {WebGLRenderTarget} writeBuffer Output + * @param {WebGLRenderTarget} readBuffer Input + * @param {boolean} maskActive filter + * @param {boolean} renderToScreen render to canvas OR buffer + * @public + */ + public render(renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean) { + renderer.getClearColor(this.oldClearColor); + this.oldClearAlpha = renderer.getClearAlpha(); + const oldAutoClear = renderer.autoClear; + renderer.autoClear = false; + + renderer.setClearColor(this.clearColor, 0); + + if (maskActive) renderer.context.disable(renderer.context.STENCIL_TEST); + + // Render input to screen + if (renderToScreen) { + this.fsQuad.setMaterial(this.basic); + this.basic.map = readBuffer.texture; + renderer.setRenderTarget(null); + renderer.clear(); + this.fsQuad.render(renderer); + } + + // 1. Extract Bright Areas + + this.highPassUniforms['tDiffuse'].value = readBuffer.texture; + this.highPassUniforms['luminosityThreshold'].value = this.threshold; + this.fsQuad.setMaterial(this.materialHighPassFilter); + + renderer.setRenderTarget(this.renderTargetBright); + renderer.clear(); + this.fsQuad.render(renderer); + + // 2. Blur All the mips progressively + + let inputRenderTarget = this.renderTargetBright; + + for (let i = 0; i < this.nMips; i++) { + this.fsQuad.setMaterial(this.separableBlurMaterials[i]); + + this.separableBlurMaterials[i].uniforms['colorTexture'].value = inputRenderTarget.texture; + this.separableBlurMaterials[i].uniforms['direction'].value = this.BlurDirectionX; + renderer.setRenderTarget(this.renderTargetsHorizontal[i]); + renderer.clear(); + this.fsQuad.render(renderer); + + this.separableBlurMaterials[i].uniforms['colorTexture'].value = this.renderTargetsHorizontal[i].texture; + this.separableBlurMaterials[i].uniforms['direction'].value = this.BlurDirectionY; + renderer.setRenderTarget(this.renderTargetsVertical[i]); + renderer.clear(); + this.fsQuad.render(renderer); + + inputRenderTarget = this.renderTargetsVertical[i]; + } + + // Composite All the mips + + this.fsQuad.setMaterial(this.compositeMaterial); + this.compositeMaterial.uniforms['bloomStrength'].value = this.strength; + this.compositeMaterial.uniforms['bloomRadius'].value = this.radius; + this.compositeMaterial.uniforms['bloomTintColors'].value = this.bloomTintColors; + + renderer.setRenderTarget(this.renderTargetsHorizontal[0]); + renderer.clear(); + this.fsQuad.render(renderer); + + // Blend it additively over the input texture + + this.fsQuad.setMaterial(this.materialCopy); + this.copyUniforms['tDiffuse'].value = this.renderTargetsHorizontal[0].texture; + + if (maskActive) renderer.context.enable(renderer.context.STENCIL_TEST); + + renderer.setRenderTarget(renderToScreen ? null : readBuffer); + this.fsQuad.render(renderer); + + // Restore renderer settings + renderer.setClearColor(this.oldClearColor, this.oldClearAlpha); + renderer.autoClear = oldAutoClear; + } + + /** + * Make seperable material + * @param {number} kernelRadius size + * @return {ShaderMaterial} + */ + private getSeperableBlurMaterial(kernelRadius) { + return new ShaderMaterial({ + + defines: { + 'KERNEL_RADIUS': kernelRadius, + 'SIGMA': kernelRadius, + }, + + uniforms: { + 'colorTexture': {value: null}, + 'texSize': {value: new Vector2(0.5, 0.5)}, + 'direction': {value: new Vector2(0.5, 0.5)}, + }, + + vertexShader: ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + }`, + + fragmentShader: + `#include + + varying vec2 vUv; + uniform sampler2D colorTexture; + uniform vec2 texSize; + uniform vec2 direction; + + float gaussianPdf(in float x, in float sigma) { + return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma; + } + void main() { + vec2 invSize = 1.0 / texSize; + float fSigma = float(SIGMA); + float weightSum = gaussianPdf(0.0, fSigma); + float alphaSum = 0.0; + vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum; + for( int i = 1; i < KERNEL_RADIUS; i ++ ) { + float x = float(i); + float w = gaussianPdf(x, fSigma); + vec2 uvOffset = direction * invSize * x; + vec4 sample1 = texture2D( colorTexture, vUv + uvOffset); + vec4 sample2 = texture2D( colorTexture, vUv - uvOffset); + diffuseSum += (sample1.rgb + sample2.rgb) * w; + alphaSum += (sample1.a + sample2.a) * w; + weightSum += 2.0 * w; + } + gl_FragColor = vec4(diffuseSum/weightSum, alphaSum/weightSum); + }`, + }); + } + + /** + * Make helper material + * @param {number} nMips MipMaps + * @return {ShaderMaterial} + */ + private getCompositeMaterial(nMips) { + return new ShaderMaterial({ + + defines: { + 'NUM_MIPS': nMips, + }, + + uniforms: { + 'blurTexture1': {value: null}, + 'blurTexture2': {value: null}, + 'blurTexture3': {value: null}, + 'blurTexture4': {value: null}, + 'blurTexture5': {value: null}, + 'dirtTexture': {value: null}, + 'bloomStrength': {value: 1.0}, + 'bloomFactors': {value: null}, + 'bloomTintColors': {value: null}, + 'bloomRadius': {value: 0.0}, + }, + + vertexShader: ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + }`, + + fragmentShader: ` + varying vec2 vUv; + + uniform sampler2D blurTexture1; + uniform sampler2D blurTexture2; + uniform sampler2D blurTexture3; + uniform sampler2D blurTexture4; + uniform sampler2D blurTexture5; + uniform sampler2D dirtTexture; + uniform float bloomStrength; + uniform float bloomRadius; + uniform float bloomFactors[NUM_MIPS]; + uniform vec3 bloomTintColors[NUM_MIPS]; + + float lerpBloomFactor(const in float factor) { + float mirrorFactor = 1.2 - factor; + return mix(factor, mirrorFactor, bloomRadius); + } + + void main() { + gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + + lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + + lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + + lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + + lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) ); + }`, + }); + } +} diff --git a/src/three/shader/BaseShader.ts b/src/three/shader/BaseShader.ts new file mode 100644 index 0000000..9d51d88 --- /dev/null +++ b/src/three/shader/BaseShader.ts @@ -0,0 +1,45 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +/** +* This is a basic shared interface for shaders. +* @public +*/ +export interface BaseShader { + + /** + * short name description for the shader + * @public + */ + shaderID: string; + + /** + * glsl vertex shader coder + * @public + */ + vertexShader: string; + + /** + * glsl fragment shader coder + * @public + */ + fragmentShader: string; + + /** + * glsl shared uniforms + * @public + */ + uniforms: any; + + /** + * glsl defines + * @public + */ + defines: any; +} diff --git a/src/three/shader/BlendShader.ts b/src/three/shader/BlendShader.ts new file mode 100644 index 0000000..2b04ab4 --- /dev/null +++ b/src/three/shader/BlendShader.ts @@ -0,0 +1,50 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +import {BaseShader} from './BaseShader'; + +/** +* Blend another texture in and out +* @public +* @implements {BaseShader} +*/ +export class BlendShader implements BaseShader { + defines = null; + + shaderID = 'blendShader'; + + uniforms = { + tDiffuse: {value: null}, + overlayBuffer: {value: null}, + mixValue: {value: 1}, + }; + + vertexShader = ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `; + + fragmentShader = ` + uniform sampler2D tDiffuse; + uniform sampler2D overlayBuffer; + + varying vec2 vUv; + + void main() { + vec4 texel1 = texture2D(tDiffuse, vUv); + vec4 texel2 = texture2D(overlayBuffer, vUv); + vec4 diff = abs(texel1 - texel2); + gl_FragColor = vec4(diff, 1.0); + } + `; +} diff --git a/src/three/shader/BlurShader.ts b/src/three/shader/BlurShader.ts new file mode 100644 index 0000000..5496978 --- /dev/null +++ b/src/three/shader/BlurShader.ts @@ -0,0 +1,74 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +import {Vector2} from 'three'; +import {BaseShader} from './BaseShader'; + +/** +* Blur shader with Alpha support +* @public +* @implements {BaseShader} +*/ +export class BlurShader implements BaseShader { + defines = null; + + shaderID = 'blurShader'; + + uniforms = { + tDiffuse: {value: null}, + iResolution: {value: new Vector2(1, 1)}, + u_sigma: {value: 0.5}, + u_dir: {value: new Vector2(0.1, 0.1)}, + }; + + vertexShader = ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + } + `; + + fragmentShader = ` + varying vec2 vUv; + + uniform sampler2D tDiffuse; + uniform vec2 iResolution; + uniform float u_sigma; + uniform vec2 u_dir; + + float CalcGauss(float x, float sigma) { + if ( sigma <= 0.0 ) return 0.0; + return exp( -(x*x) / (2.0 * sigma) ) / (2.0 * 3.14157 * sigma); + } + + void main() { + vec2 texC = vUv; + vec4 texCol = texture2D( tDiffuse, texC ); + vec4 gaussCol = vec4( texCol.rgb, 1.0 ); + float alphaV = texCol.a; + vec2 step = u_dir / iResolution; + for (int i = 1; i <= 32; ++ i) + { + float weight = CalcGauss(float(i) / 32.0, u_sigma * 0.5); + if (weight < 1.0/255.0) break; + texCol = texture2D(tDiffuse, texC + step * float(i)); + gaussCol += vec4(texCol.rgb * weight, weight); + alphaV += texCol.a * weight; + texCol = texture2D(tDiffuse, texC - step * float(i)); + gaussCol += vec4(texCol.rgb * weight, weight); + alphaV += texCol.a * weight; + } + alphaV = clamp(alphaV / gaussCol.w, 0.0, 1.0); + gaussCol.rgb = clamp(gaussCol.rgb / gaussCol.w, 0.0, 1.0); + gl_FragColor = vec4(gaussCol.rgb, alphaV); + } + `; +} diff --git a/src/three/shader/ChromaticShader.ts b/src/three/shader/ChromaticShader.ts new file mode 100644 index 0000000..ee39998 --- /dev/null +++ b/src/three/shader/ChromaticShader.ts @@ -0,0 +1,60 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +import {Vector2} from 'three'; +import {BaseShader} from './BaseShader'; + +/** +* Chromatic Abberation shader with alpha support +* @public +* @implements {BaseShader} +*/ +export class ChromaticShader implements BaseShader { + defines = null; + + shaderID = 'chromaticShader'; + + uniforms = { + tDiffuse: {value: null}, + iResolution: {value: new Vector2(1, 1)}, + strength: {value: 10.0}, + }; + + vertexShader = ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + } + `; + + fragmentShader = ` + uniform sampler2D tDiffuse; + uniform vec2 iResolution; + uniform float strength; + + varying vec2 vUv; + + vec4 ca(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { + vec4 col = vec4(0.0); + vec2 off = vec2(1.33333333333333) * direction; + col.ra = texture2D(image, uv).ra; + col.g = texture2D(image, uv - (off / resolution)).g; + col.b = texture2D(image, uv - 2. * (off / resolution)).b; + return col; + } + + void main() { + vec2 uv = gl_FragCoord.xy / iResolution; + vec2 direction = (uv - .5) * strength; + gl_FragColor = ca(tDiffuse, uv, iResolution.xy, direction); + } + `; +}; diff --git a/src/three/shader/CopyShader.ts b/src/three/shader/CopyShader.ts new file mode 100644 index 0000000..300c8f5 --- /dev/null +++ b/src/three/shader/CopyShader.ts @@ -0,0 +1,49 @@ +/** +* @author alteredq / http://alteredqualia.com/ +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +import {BaseShader} from './BaseShader'; + +/** +* Siimple I/O shader +* @public +* @implements {BaseShader} +*/ +export class CopyShader implements BaseShader { + defines = null; + + shaderID = 'copyShader'; + + uniforms = { + tDiffuse: {value: null}, + opacity: {value: 1.0}, + } + + vertexShader = ` + varying vec2 vUv; + + void main() { + + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + } + `; + + fragmentShader = ` + uniform float opacity; + uniform sampler2D tDiffuse; + varying vec2 vUv; + + void main() { + + vec4 texel = texture2D( tDiffuse, vUv ); + gl_FragColor = opacity * texel; + } + `; +} diff --git a/src/three/shader/FXAAShader.ts b/src/three/shader/FXAAShader.ts new file mode 100644 index 0000000..f6f1150 --- /dev/null +++ b/src/three/shader/FXAAShader.ts @@ -0,0 +1,876 @@ +/** +* @author alteredq / http://alteredqualia.com/ +* @author davidedc / http://www.sketchpatch.net/ +* @author hexxone / https://hexx.one +*/ + +import {Vector2} from 'three'; +import {BaseShader} from './BaseShader'; + +/** +* NVIDIA FXAA by Timothy Lottes +* http://timothylottes.blogspot.com/2011/06/fxaa3-source-released.html +* - WebGL port by @supereggbert +* http://www.glge.org/demos/fxaa/ +* +* @public +* @implements {BaseShader} +*/ +export class FXAAShader implements BaseShader { + defines = null; + + shaderID = 'fxaaShader'; + + uniforms = { + tDiffuse: {value: null}, + resolution: {value: new Vector2(1 / 1024, 1 / 512)}, + } + + vertexShader = ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + } + `; + + fragmentShader = ` + + uniform sampler2D tDiffuse; + uniform vec2 resolution; + varying vec2 vUv; + + // FXAA 3.11 implementation by NVIDIA, ported to WebGL by Agost Biro (biro@archilogic.com) + //-------------- + // File: es3-keplerFXAAassetsshaders/FXAA_DefaultES.frag + // SDK Version: v3.00 + // Email: gameworks@nvidia.com + // Site: http://developer.nvidia.com/ + // + // Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions + // are met: + // * Redistributions of source code must retain the above copyright + // notice, this list of conditions and the following disclaimer. + // * Redistributions in binary form must reproduce the above copyright + // notice, this list of conditions and the following disclaimer in the + // documentation and/or other materials provided with the distribution. + // * Neither the name of NVIDIA CORPORATION nor the names of its + // contributors may be used to endorse or promote products derived + // from this software without specific prior written permission. + // + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + // + //-------------- + + #define FXAA_PC 1 + #define FXAA_GLSL_100 1 + #define FXAA_QUALITY_PRESET 12 + #define FXAA_GREEN_AS_LUMA 1 + /* ===== */ + #ifndef FXAA_PC_CONSOLE + #define FXAA_PC_CONSOLE 0 + #endif + /* ===== */ + #ifndef FXAA_GLSL_120 + #define FXAA_GLSL_120 0 + #endif + /* ===== */ + #ifndef FXAA_GLSL_130 + #define FXAA_GLSL_130 0 + #endif + /* ===== */ + #ifndef FXAA_HLSL_3 + #define FXAA_HLSL_3 0 + #endif + /* ===== */ + #ifndef FXAA_HLSL_4 + #define FXAA_HLSL_4 0 + #endif + /* ===== */ + #ifndef FXAA_HLSL_5 + #define FXAA_HLSL_5 0 + #endif + /* ========== */ + #ifndef FXAA_GREEN_AS_LUMA + #define FXAA_GREEN_AS_LUMA 0 + #endif + /* ===== */ + #ifndef FXAA_EARLY_EXIT + #define FXAA_EARLY_EXIT 1 + #endif + /* ===== */ + #ifndef FXAA_DISCARD + #define FXAA_DISCARD 0 + #endif + /* ===== */ + #ifndef FXAA_FAST_PIXEL_OFFSET + #ifdef GL_EXT_gpu_shader4 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifndef FXAA_FAST_PIXEL_OFFSET + #define FXAA_FAST_PIXEL_OFFSET 0 + #endif + #endif + /* ===== */ + #ifndef FXAA_GATHER4_ALPHA + #if (FXAA_HLSL_5 == 1) + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifndef FXAA_GATHER4_ALPHA + #define FXAA_GATHER4_ALPHA 0 + #endif + #endif + + /*========== + FXAA QUALITY - TUNING KNOBS + ---------- + NOTE the other tuning knobs are now in the shader function inputs! + ==========*/ + #ifndef FXAA_QUALITY_PRESET + #define FXAA_QUALITY_PRESET 12 + #endif + + /*========== + FXAA QUALITY - PRESETS + ==========*/ + /*========== + FXAA QUALITY - MEDIUM DITHER PRESETS + ==========*/ + #if (FXAA_QUALITY_PRESET == 10) + #define FXAA_QUALITY_PS 3 + #define FXAA_QUALITY_P0 1.5 + #define FXAA_QUALITY_P1 3.0 + #define FXAA_QUALITY_P2 12.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 11) + #define FXAA_QUALITY_PS 4 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 3.0 + #define FXAA_QUALITY_P3 12.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 12) + #define FXAA_QUALITY_PS 5 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 4.0 + #define FXAA_QUALITY_P4 12.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 13) + #define FXAA_QUALITY_PS 6 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 4.0 + #define FXAA_QUALITY_P5 12.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 14) + #define FXAA_QUALITY_PS 7 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 4.0 + #define FXAA_QUALITY_P6 12.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 15) + #define FXAA_QUALITY_PS 8 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 4.0 + #define FXAA_QUALITY_P7 12.0 + #endif + /*========== + FXAA QUALITY - LOW DITHER PRESETS + ==========*/ + #if (FXAA_QUALITY_PRESET == 20) + #define FXAA_QUALITY_PS 3 + #define FXAA_QUALITY_P0 1.5 + #define FXAA_QUALITY_P1 2.0 + #define FXAA_QUALITY_P2 8.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 21) + #define FXAA_QUALITY_PS 4 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 8.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 22) + #define FXAA_QUALITY_PS 5 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 8.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 23) + #define FXAA_QUALITY_PS 6 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 8.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 24) + #define FXAA_QUALITY_PS 7 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 3.0 + #define FXAA_QUALITY_P6 8.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 25) + #define FXAA_QUALITY_PS 8 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 4.0 + #define FXAA_QUALITY_P7 8.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 26) + #define FXAA_QUALITY_PS 9 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 4.0 + #define FXAA_QUALITY_P8 8.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 27) + #define FXAA_QUALITY_PS 10 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 4.0 + #define FXAA_QUALITY_P9 8.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 28) + #define FXAA_QUALITY_PS 11 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 4.0 + #define FXAA_QUALITY_P10 8.0 + #endif + /* ===== */ + #if (FXAA_QUALITY_PRESET == 29) + #define FXAA_QUALITY_PS 12 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 2.0 + #define FXAA_QUALITY_P10 4.0 + #define FXAA_QUALITY_P11 8.0 + #endif + /*========== + FXAA QUALITY - EXTREME QUALITY + ==========*/ + #if (FXAA_QUALITY_PRESET == 39) + #define FXAA_QUALITY_PS 12 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.0 + #define FXAA_QUALITY_P2 1.0 + #define FXAA_QUALITY_P3 1.0 + #define FXAA_QUALITY_P4 1.0 + #define FXAA_QUALITY_P5 1.5 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 2.0 + #define FXAA_QUALITY_P10 4.0 + #define FXAA_QUALITY_P11 8.0 + #endif + + /*========== + API PORTING + ==========*/ + #if (FXAA_GLSL_100 == 1) || (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) + #define FxaaBool bool + #define FxaaDiscard discard + #define FxaaFloat float + #define FxaaFloat2 vec2 + #define FxaaFloat3 vec3 + #define FxaaFloat4 vec4 + #define FxaaHalf float + #define FxaaHalf2 vec2 + #define FxaaHalf3 vec3 + #define FxaaHalf4 vec4 + #define FxaaInt2 ivec2 + #define FxaaSat(x) clamp(x, 0.0, 1.0) + #define FxaaTex sampler2D + #else + #define FxaaBool bool + #define FxaaDiscard clip(-1) + #define FxaaFloat float + #define FxaaFloat2 float2 + #define FxaaFloat3 float3 + #define FxaaFloat4 float4 + #define FxaaHalf half + #define FxaaHalf2 half2 + #define FxaaHalf3 half3 + #define FxaaHalf4 half4 + #define FxaaSat(x) saturate(x) + #endif + /* ===== */ + #if (FXAA_GLSL_100 == 1) + #define FxaaTexTop(t, p) texture2D(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r), 0.0) + #endif + /* ===== */ + #if (FXAA_GLSL_120 == 1) + #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) + #if (FXAA_FAST_PIXEL_OFFSET == 1) + #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) + #else + #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) + #endif + #if (FXAA_GATHER4_ALPHA == 1) + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif + #endif + /* ===== */ + #if (FXAA_GLSL_130 == 1) + #define FxaaTexTop(t, p) textureLod(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + #if (FXAA_GATHER4_ALPHA == 1) + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif + #endif + /* ===== */ + #if (FXAA_HLSL_3 == 1) + #define FxaaInt2 float2 + #define FxaaTex sampler2D + #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0)) + #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0)) + #endif + /* ===== */ + #if (FXAA_HLSL_4 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) + #endif + /* ===== */ + #if (FXAA_HLSL_5 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) + #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) + #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) + #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) + #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) + #endif + + /*========== + GREEN AS LUMA OPTION SUPPORT FUNCTION + ==========*/ + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } + #else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } + #endif + + + /*========== + FXAA3 QUALITY - PC + ==========*/ + #if (FXAA_PC == 1) + /* ===== */ + FxaaFloat4 FxaaPixelShader( + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + ) { + /* ===== */ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #if (FXAA_GLSL_100 == 1) + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 0.0, 1.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0, 0.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 0.0,-1.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0, 0.0), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif + #endif + /* ===== */ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; + /* ===== */ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif + /* ===== */ + #if (FXAA_GATHER4_ALPHA == 0) + #if (FXAA_GLSL_100 == 1) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0,-1.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0, 1.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0,-1.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0, 1.0), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif + /* ===== */ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; + /* ===== */ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; + /* ===== */ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; + /* ===== */ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; + /* ===== */ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; + /* ===== */ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); + /* ===== */ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; + /* ===== */ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY_P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY_P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY_P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY_P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); + /* ===== */ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; + /* ===== */ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P1; + /* ===== */ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P2; + /* ===== */ + #if (FXAA_QUALITY_PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P3; + /* ===== */ + #if (FXAA_QUALITY_PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P4; + /* ===== */ + #if (FXAA_QUALITY_PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P5; + /* ===== */ + #if (FXAA_QUALITY_PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P6; + /* ===== */ + #if (FXAA_QUALITY_PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P7; + /* ===== */ + #if (FXAA_QUALITY_PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P8; + /* ===== */ + #if (FXAA_QUALITY_PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P9; + /* ===== */ + #if (FXAA_QUALITY_PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P10; + /* ===== */ + #if (FXAA_QUALITY_PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P11; + /* ===== */ + #if (FXAA_QUALITY_PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P12; + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + /* ===== */ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; + /* ===== */ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; + /* ===== */ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; + /* ===== */ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + #endif + } + /* ========== */ + #endif + void main() { + gl_FragColor = FxaaPixelShader( + vUv, + vec4(0.0), + tDiffuse, + tDiffuse, + tDiffuse, + resolution, + vec4(0.0), + vec4(0.0), + vec4(0.0), + 0.75, + 0.166, + 0.0833, + 0.0, + 0.0, + 0.0, + vec4(0.0) + ); + gl_FragColor.a = texture2D(tDiffuse, vUv).a; + }`; +} + + diff --git a/src/three/shader/FractalMirrorShader.ts b/src/three/shader/FractalMirrorShader.ts new file mode 100644 index 0000000..8109550 --- /dev/null +++ b/src/three/shader/FractalMirrorShader.ts @@ -0,0 +1,72 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +* +* @description +*/ + +import {Vector2} from 'three'; +import {BaseShader} from './BaseShader'; + +/** +* Customized Kaleidoscope shader +* Inspired by ackleyrc: https://www.shadertoy.com/view/llXcRl +* +* @public +* @implements {BaseShader} +*/ +export class FractalMirrorShader implements BaseShader { + defines = null; + + shaderID = 'fractalMirror'; + + uniforms = { + tDiffuse: {value: null}, + iResolution: {value: new Vector2(16, 9)}, + numSides: {value: 2.0}, // minimum value + invert: {value: false}, + }; + + vertexShader = ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + } + `; + + fragmentShader = ` + uniform sampler2D tDiffuse; + uniform vec2 iResolution; + uniform float numSides; + uniform bool invert; + + varying vec2 vUv; + + const float PI = 3.14159265358979323846; + + void main() { + vec2 center = vec2(0.5, 0.5); + float zoom = iResolution.x / iResolution.y; + vec2 uv = center - vUv; + if(zoom > 1.0) uv.y /= zoom; + else uv.x *= zoom; + + float KA = PI / numSides; + float angle = abs(mod(atan(uv.y, uv.x), 2.0 * KA) - KA); + if(zoom > 1.0) angle -= 45.0; + vec2 transformed = length(uv) * vec2(sin(angle), cos(angle)); + if(!invert) transformed += center; + else { + if(transformed.x < 0.0) transformed.x += 1.0; + if(transformed.y < 0.0) transformed.y += 1.0; + } + gl_FragColor = texture2D(tDiffuse, transformed); + } + `; +} diff --git a/src/three/shader/LUTShader.ts b/src/three/shader/LUTShader.ts new file mode 100644 index 0000000..228aa0d --- /dev/null +++ b/src/three/shader/LUTShader.ts @@ -0,0 +1,82 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +import {BaseShader} from './BaseShader'; + +/** +* LookUpTable shader +* taken from ThreeJS examples and converted to TS +* +* @public +* @implements {BaseShader} +*/ +export class LUTShader implements BaseShader { + defines = null; + + shaderID = 'LUTShader'; + + uniforms = { + tDiffuse: {value: null}, + lutMap: {value: null}, + lutMapSize: {value: 1}, + }; + + vertexShader = ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + } + `; + + fragmentShader = ` + #include + + #define FILTER_LUT true + + uniform sampler2D tDiffuse; + uniform sampler2D lutMap; + uniform float lutMapSize; + + varying vec2 vUv; + + vec4 sampleAs3DTexture(sampler2D tex, vec3 texCoord, float size) { + float sliceSize = 1.0 / size; // space of 1 slice + float slicePixelSize = sliceSize / size; // space of 1 pixel + float width = size - 1.0; + float sliceInnerSize = slicePixelSize * width; // space of size pixels + float zSlice0 = floor( texCoord.z * width); + float zSlice1 = min( zSlice0 + 1.0, width); + float xOffset = slicePixelSize * 0.5 + texCoord.x * sliceInnerSize; + float yRange = (texCoord.y * width + 0.5) / size; + float s0 = xOffset + (zSlice0 * sliceSize); + + #ifdef FILTER_LUT + + float s1 = xOffset + (zSlice1 * sliceSize); + vec4 slice0Color = texture2D(tex, vec2(s0, yRange)); + vec4 slice1Color = texture2D(tex, vec2(s1, yRange)); + float zOffset = mod(texCoord.z * width, 1.0); + return mix(slice0Color, slice1Color, zOffset); + + #else + + return texture2D(tex, vec2( s0, yRange)); + + #endif + } + + void main() { + vec4 originalColor = texture2D(tDiffuse, vUv); + vec4 tempColor = sampleAs3DTexture(lutMap, originalColor.xyz, lutMapSize); + tempColor.a = originalColor.a; + gl_FragColor = tempColor; + } + `; +} diff --git a/src/three/shader/LUTShaderNearest.ts b/src/three/shader/LUTShaderNearest.ts new file mode 100644 index 0000000..701e89a --- /dev/null +++ b/src/three/shader/LUTShaderNearest.ts @@ -0,0 +1,22 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +import {LUTShader} from './LUTShader'; + +/** +* LookUpTable shader without filtering +* +* @public +* @extends {LUTShader} +*/ +export class LUTShaderNearest extends LUTShader { + shaderID = 'LUTShaderNearest'; + + fragmentShader = new LUTShader().fragmentShader.replace('#define FILTER_LUT', '//'); +} diff --git a/src/three/shader/LuminosityHighPassShader.ts b/src/three/shader/LuminosityHighPassShader.ts new file mode 100644 index 0000000..c88e7a7 --- /dev/null +++ b/src/three/shader/LuminosityHighPassShader.ts @@ -0,0 +1,62 @@ +/** +* @author bhouston / http://clara.io/ +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +*/ + +import {Color} from 'three'; +import {BaseShader} from './BaseShader'; + +/** +* Luminosity +* http://en.wikipedia.org/wiki/Luminosity +* +* @public +* @implements {BaseShader} +*/ +export class LuminosityHighPassShader implements BaseShader { + defines = null; + + shaderID = 'luminosityHighPass'; + + uniforms = { + tDiffuse: {value: null}, + luminosityThreshold: {value: 1.0}, + smoothWidth: {value: 1.0}, + defaultColor: {value: new Color(0x000000)}, + defaultOpacity: {value: 0.0}, + }; + + vertexShader = ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + } + `; + + fragmentShader = ` + uniform sampler2D tDiffuse; + uniform vec3 defaultColor; + uniform float defaultOpacity; + uniform float luminosityThreshold; + uniform float smoothWidth; + + varying vec2 vUv; + + void main() { + + vec4 texel = texture2D( tDiffuse, vUv ); + vec3 luma = vec3( 0.299, 0.587, 0.114 ); + float v = dot( texel.xyz, luma ); + vec4 outputColor = vec4( defaultColor.rgb, defaultOpacity ); + float alpha = smoothstep( luminosityThreshold, luminosityThreshold + smoothWidth, v ); + gl_FragColor = mix( outputColor, texel, alpha ); + } + `; +}; diff --git a/src/wasc-worker b/src/wasc-worker index 1ff845d..ed5803c 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 1ff845da6c841ceb94ab970dfa0ca3cd559a5432 +Subproject commit ed5803c3c0ced0fdd5f3cc8d6afecb5024adeef8 From f54090c6a0635d0b24817e7537b0ac8861291bf6 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Mon, 21 Jun 2021 14:48:02 +0200 Subject: [PATCH 31/76] Added shader precompilation & ICUE definitions; Fixed VR & worker buffer transfers --- src/ICUE.d.ts | 234 +++++++++++++++++++ src/Smallog.ts | 2 +- src/Util.ts | 51 ++++ src/WEAS.ts | 21 +- src/WEICUE.ts | 47 ++-- src/WarnHelper.ts | 2 +- src/three/EffectComposer.ts | 53 +++-- src/three/pass/BasePass.ts | 4 + src/three/pass/FullScreenHelper.ts | 8 + src/three/pass/RenderPass.ts | 10 +- src/three/pass/ShaderPass.ts | 8 + src/three/pass/UnrealBloomPass.ts | 8 + src/three/shader/LuminosityHighPassShader.ts | 2 +- src/wasc-worker | 2 +- 14 files changed, 391 insertions(+), 61 deletions(-) create mode 100644 src/ICUE.d.ts diff --git a/src/ICUE.d.ts b/src/ICUE.d.ts new file mode 100644 index 0000000..d01678b --- /dev/null +++ b/src/ICUE.d.ts @@ -0,0 +1,234 @@ +/** +* @author 'Andrew' / https://github.com/profezzional +* +* Types for Wallpaper Engine integation for CUE SDK 3.0.+ +* +* @see https://wallpaper-engine.fandom.com/wiki/Web_Wallpaper_iCUE_Reference for reference +* @see http://forum.corsair.com/v3/showthread.php?t=179027 for the latest SDK version +* @see https://github.com/profezzional/iCUE-wallpaper-engine for orginal source +*/ + +/** +* Contains information about SDK and CUE versions. +*/ +type ProtocolDetails = { + /** + * Boolean value that specifies if there were breaking changes between version of protocol implemented by server and client. + */ + breakingChanges: boolean, + + /** + * Integer number that specifies version of protocol that is implemented by current SDK. + * Numbering starts from 1. Always contains valid value even if there was no CUE found. + */ + sdkProtocolVersion: number, + + /** + * String containing version of SDK (like "1.0.0.1"). Always contains valid value even if there was no CUE found. + */ + sdkVersion: string, + + /** + * Integer number that specifies version of protocol that is implemented by CUE. + * Numbering starts from 1. If CUE was not found then this value will be 0. + */ + serverProtocolVersion: number, + + /** + * String containing version of CUE (like "1.0.0.1") or NULL if CUE was not found. + */ + serverVersion: number +}; + +/** +* Contains list of available device types +*/ +type DeviceType = 'CDT_Keyboard' | 'CDT_Mouse' | 'CDT_Headset' | 'CDT_Mousemat' | 'CDT_HeadsetStand' | 'CDT_CommanderPro' | 'CDT_LightingNodePro'; + +/** +* Valid values for keyboard physical layouts. +*/ +type KeyboardPhysicalLayout = 'CPL_US' | 'CPL_UK' | 'CPL_JP' | 'CPL_KR' | 'CPL_BR'; + +/** +* Valid values for mouse physical layouts, number represents configurable mouse LEDs. +*/ +type MousePhysicalLayout = 'CPL_Zones1' | 'CPL_Zones2' | 'CPL_Zones3' | 'CPL_Zones4'; + +/** +* Contains list of available physical layouts for keyboards and mice. +*/ +type PhysicalLayout = KeyboardPhysicalLayout | MousePhysicalLayout | 'CPL_Invalid'; + +/** +* Contains list of available logical layouts for keyboards. +*/ +type KeyboardLogicalLayout = 'CLL_US_Int' | 'CLL_NA' | 'CLL_EU' | 'CLL_UK' | 'CLL_BE' | 'CLL_BR' | 'CLL_CH' | 'CLL_CN' | 'CLL_DE' + | 'CLL_ES' | 'CLL_FR' | 'CLL_IT' | 'CLL_ND' | 'CLL_RU4' | 'CLL_JP' | 'CLL_KR' | 'CLL_TW' | 'CLL_MEX'; + +/** +* Contains list of available logical layouts for keyboards. +*/ +type LogicalLayout = KeyboardLogicalLayout | 'CLL_Invalid'; + +/** +* Contains information about device. +*/ +type DeviceInfo = { + /** + * ICUE Device ID (not set by default) + */ + id: number, + + /** + * Enum describing device type. + */ + type: DeviceType, + + /** + * Device model (like "K95RGB"). + */ + model: string, + + /** + * Enum describing physical layout of the keyboard or mouse. + * If device is neither keyboard nor mouse then value is "CPL_Invalid". + */ + physicalLayout: PhysicalLayout; + + /** + * Enum describing logical layout of the keyboard as set in CUE settings. + * If device is not keyboard then value is "CLL_Invalid". + */ + logicalLayout: LogicalLayout; + + /** + * Number of controllable LEDs on the device. + */ + ledCount: number; + + /** + * Led positions on the device (not set by default) + */ + leds: LedPosition[]; + + /** + * Contains list of device capabilities. + * First version of SDK only supports lighting, but future versions may also support other capabilities. + */ + capsMask: { CDC_Lighting: boolean } | { CDC_None: boolean }; +}; + +/** +* Contains led id and position of led rectangle. Most of the keys are rectangular. +* In case if key is not rectangular (like Enter in ISO/UK layout) it returns the smallest rectangle that fully contains the key. +*/ +type LedPosition = { + /** + * For keyboards, mousemats and headset stands, height in mm; + * for DIY-devices, height in logical units. + */ + height: number, + + /** + * For keyboards, mousemats and headset stands, width in mm; + * for DIY-devices, width in logical units. + */ + width: number, + + /** + * For keyboards, mousemats and headset stands, top offset in mm; + * for DIY-devices, top offset in logical units. + */ + top: number, + + /** + * For keyboards, mousemats and headset stands, left offset in mm; + * for DIY-devices, left offset in logical units. + */ + left: number, + + /** + * Identifier of led. + */ + ledId: number, + + /** + * Identifier of led. + */ + ledIdName: string +}; + +/** Contains information about led and its color. */ +type LedColor = { + /** + * Identifier of LED to set. + */ + ledId: number, + + /** + * Red brightness [0..255]. + */ + r: number, + + /** + * Green brightness [0..255]. + */ + g: number, + + /** + * Blue brightness [0..255]. + */ + b: number +}; + +/** Main Interface */ +export interface ICUE { + /** + * Returns current status and version of iCUE SDK. + * @param callback A callback into which the protocol details are passed. + */ + getProtocolDetails(callback: (protocolDetails: ProtocolDetails) => void): void; + + /** + * Returns the number of recognized iCUE compatible devices on the system. + * @param callback A callback into which the device count is passed. + */ + getDeviceCount(callback: (count: number) => void): void; + + /** + * Returns all information specific to a single device. + * @param deviceIndex The index of the device about which to get info. + * @param callback A callback into which the device info is passed. + */ + getDeviceInfo(deviceIndex: number, callback: (deviceInfo: DeviceInfo) => void): void; + + /** + * Provides list of keyboard, mousemat, headset stand and DIY-devices LEDs with their physical (keyboard, mousemat and headset stand) or logical (DIY-devices) positions. + * @param deviceIndex The index of the device whose LED position to get. + * @param callback A callback into which the LED positions are passed. + */ + getLedPositionsByDeviceIndex(deviceIndex: number, callback: (leds: LedPosition[]) => void): void; + + /** + * Set specified leds to some colors. + * The color is retained until changed by successive calls. + * This function does not take logical layout into account, and returns control to the caller immediately. + * @param leds Array containing colors for each LED. + */ + setLedsColorsAsync(leds: LedColor[]): void; + + /** + * Updates all LEDs for given devices to one specific color. + * @param deviceIndexOrArray Index or indices of the device(s) whose LEDs to set to the specified color. + * @param ledColor The color to which to change the LEDs of the specified device(s). + */ + setAllLedsColorsAsync(deviceIndexOrArray: number | number[], ledColor: LedColor): void; + + /** + * Updates all LEDs for given devices to one specific color. + * @param deviceIndexOrArray Index or indices of the device(s) whose LEDs to set to the specified color. + * @param ledColor The color to which to change the LEDs of the specified device(s). + */ + setLedColorsByImageData(deviceIndexOrArray: number | number[], encodedImageData, width: number, height: number): void; +} diff --git a/src/Smallog.ts b/src/Smallog.ts index 0c7181a..6ec3428 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -56,7 +56,7 @@ export enum LogLevel { * @public */ export module Smallog { - const logLevel: LogLevel = 2; + const logLevel: LogLevel = LogLevel.Info; let preFix: string = '[Smallog] '; let printTime: boolean = false; diff --git a/src/Util.ts b/src/Util.ts index 96cdad3..bfee9e1 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -34,3 +34,54 @@ export function waitReady() { promQueue.push(resolve); }); } + +/** +* @todo check for rgba errors +* Convert helper +* @param {string} r_g_b format: "r g b" where each is float 0-1 +* @param {number} mlt multiplier (default 255) +* @return {Object} {r,g,b,a} with float 0-mlt +*/ +export function rgbToObj(r_g_b:string, mlt = 255): {r: number, g:number, b:number, a:number} { + // fix support for rgba strings + const brackI = r_g_b.indexOf('('); + if (brackI > -1) { + r_g_b = r_g_b.substring(brackI + 1, r_g_b.indexOf(')')); + } + // do splitting and multiplying + const spl = r_g_b.split(' ') as any[]; + for (let i = 0; i < spl.length; i++) { + spl[i] = isNaN(spl[i]) ? 0 : Math.min(mlt, Math.max(0, spl[i] * mlt)); + } + return { + r: spl[0] || 0, + g: spl[1] || 0, + b: spl[2] || 0, + a: spl[3] || (1 * mlt), + }; +} + +/** +* Convert helper +* @param {string} r_g_b format: "r g b" where each is float 0-1 +* @return {Object} {h,s,l} with float 0-1 +*/ +export function rgbToHSL(r_g_b: string): {h: number, s:number, l:number} { + const cO = rgbToObj(r_g_b, 1); + const ma = Math.max(cO.r, cO.g, cO.b); + const mi = Math.min(cO.r, cO.g, cO.b); + const hsl = {h: 0, s: 0, l: (mi + ma) / 2}; + const t = ma - mi; + switch (hsl.s = hsl.l <= .5 ? t / (ma + mi) : t / (2 - ma - mi), ma) { + case cO.r: + hsl.h = (cO.g - cO.b) / t + (cO.g < cO.b ? 6 : 0); + break; + case cO.g: + hsl.h = (cO.b - cO.r) / t + 2; + break; + case cO.b: + hsl.h = (cO.r - cO.g) / t + 4; + } + hsl.h /= 6; + return hsl; +} diff --git a/src/WEAS.ts b/src/WEAS.ts index 6c09ed5..143ebf1 100644 --- a/src/WEAS.ts +++ b/src/WEAS.ts @@ -206,31 +206,35 @@ export class WEAS extends CComponent { run(({module, instance, exports, params}) => { const ex = instance.exports as any; const {data} = params[0]; + const arrData = new Float64Array(data); // set audio data directly in module memory - exports.__getFloat64ArrayView(ex.inputData).set(data); + exports.__getFloat64ArrayView(ex.inputData).set(arrData); // trigger processing processing ex.update(); // get copy of updated Data & Properties const r = { // => result - data: new Float64Array(exports.__getFloat64ArrayView(ex.outputData)), - props: new Float64Array(exports.__getFloat64ArrayView(ex.audioProps)), + data: new Float64Array(exports.__getFloat64ArrayView(ex.outputData)).buffer, + props: new Float64Array(exports.__getFloat64ArrayView(ex.audioProps)).buffer, }; return r; }, // params passed to worker { - data: self.inBuff, + data: self.inBuff.buffer, }) // worker result, back in main context .then((result) => { const {data, props} = result; - const realProps = self.getProps(props); + const arrData = new Float64Array(data); + const arrProps = new Float64Array(props); + + const realProps = self.getProps(arrProps); const teim = performance.now() - start; // apply actual last Data from worker self.lastAudio = { time: start, ellapsed: teim, - data, + data: arrData, ...realProps, } as any; // print info @@ -345,11 +349,12 @@ export class WEAS extends CComponent { run(({module, instance, exports, params}) => { const ex = instance.exports as any; const {data} = params[0]; - exports.__getFloat64ArrayView(ex.audioSettings).set(data); + const arrDat = new Float64Array(data); + exports.__getFloat64ArrayView(ex.audioSettings).set(arrDat); }, // Data passed to worker { - data: sett, + data: sett.buffer, }) // Back to main context .then(() => { diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 068294c..2fc87b6 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -9,9 +9,10 @@ import {CComponent} from './CComponent'; import {CSettings} from './CSettings'; -import {waitReady} from './Util'; +import {rgbToObj, waitReady} from './Util'; import {Smallog} from './Smallog'; import {WEAS} from './WEAS'; +import {ICUE} from './ICUE'; const IMG_SRC = './img/icue.png'; @@ -51,7 +52,8 @@ export class CUESettings extends CSettings { * @extends {CComponent} */ export class WEICUE extends CComponent { - private weas: WEAS = null; + private cue: ICUE; + private weas: WEAS; private holder: HTMLDivElement = null; private texter: HTMLDivElement = null; @@ -84,13 +86,15 @@ export class WEICUE extends CComponent { onPluginLoaded: (name: string, version: string) => { const lower = name.toLocaleLowerCase(); if (lower === 'cue' || lower === 'led') { + this.cue = window['cue']; this.isAvailable = true; Smallog.debug(`Plugin loaded: ${name}, v${version}`, ClassName); } }, }; + + // inject helpers waitReady().then(() => { - // inject helpers this.injectCSS(); this.injectHTML(); this.init(); @@ -320,7 +324,7 @@ export class WEICUE extends CComponent { * @param {number} count Retries (will stop at 100) * @ignore */ - private initCUE(count) { + private initCUE(count: number) { // wait for plugins if (!this.isAvailable) { if (count < 100) setTimeout(() => this.initCUE(++count), 150); @@ -330,13 +334,13 @@ export class WEICUE extends CComponent { // setup devices this.icueDevices = []; - window['cue'].getDeviceCount((deviceCount) => { + this.cue.getDeviceCount((deviceCount) => { this.icueMessage('LED: Found ' + deviceCount + ' devices.'); for (let xi = 0; xi < deviceCount; xi++) { const xl = xi; - window['cue'].getDeviceInfo(xl, (info) => { + this.cue.getDeviceInfo(xl, (info) => { info.id = xl; - window['cue'].getLedPositionsByDeviceIndex(xl, (leds) => { + this.cue.getLedPositionsByDeviceIndex(xl, (leds) => { info.leds = leds; this.icueDevices[xl] = info; }); @@ -346,7 +350,7 @@ export class WEICUE extends CComponent { } /** - * do the thing... + * do the thing... * @ignore */ private updateFrame() { @@ -358,31 +362,22 @@ export class WEICUE extends CComponent { const encDat = this.getEncodedCanvasImageData(this.helperContext.getImageData(0, 0, canvasX, canvasY)); // update all icueDevices with data for (let xi = 0; xi < this.icueDevices.length; xi++) { - window['cue'].setLedColorsByImageData(xi, encDat, canvasX, canvasY); + this.cue.setLedColorsByImageData(xi, encDat, canvasX, canvasY); } } // color mode if (sett.icue_mode == 2) { - // get lol objects - const col = sett.icue_main_color.split(' ') as unknown[]; - let ledColor = { - r: col[0] as number * 255, - g: col[1] as number * 255, - b: col[2] as number * 255, - }; ; // try audio multiplier processing + let mlt = 255; if (this.weas.hasAudio()) { const aud = this.weas.lastAudio; - const mlt = 255 * aud.average / aud.range / aud.intensity * 10; - ledColor = { - r: Math.min(255, Math.max(0, col[0] as number * mlt)), - g: Math.min(255, Math.max(0, col[1] as number * mlt)), - b: Math.min(255, Math.max(0, col[2] as number * mlt)), - }; + mlt *= aud.average / aud.range / aud.intensity * 10; } + // get lol objects + const ledCol = rgbToObj(sett.icue_main_color, mlt); // update all icueDevices with data for (let xi = 0; xi < this.icueDevices.length; xi++) { - window['cue'].setAllLedsColorsAsync(xi, ledColor); + this.cue.setAllLedsColorsAsync(xi, ledCol as any); } } } @@ -401,10 +396,8 @@ export class WEICUE extends CComponent { const area: any = this.getArea(false); const hctx = this.helperContext; // get real rgb values - const spl = sett.main_color.split(' ') as unknown[]; - for (let i = 0; i < spl.length; i++) spl[i] = (spl[i] as number * 255); - // overlay "decay" style - hctx.fillStyle = 'rgba(' + spl.join(', ') + ', ' + sett.icue_area_decay / 100 + ')'; + const cO = rgbToObj(sett.main_color); + hctx.fillStyle = `rgba(${cO.r}, ${cO.g}, ${cO.b}, ${sett.icue_area_decay / 100})`; hctx.fillRect(0, 0, canvasX, canvasY); // scale down and copy the image to the helper canvas hctx.drawImage(mainCanvas, area.left, area.top, area.width, area.height, 0, 0, canvasX, canvasY); diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index df3878f..2649671 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -129,7 +129,7 @@ export class WarnHelper extends CComponent { public updateSettings(): Promise { // fix for instantly removing the warning while it shows if (!this.settings.seizure_warning && this.element.classList.contains('show')) { - return this.hide(); + this.hide(); } // whatever return Promise.resolve(); diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts index 3575cf1..223cf0a 100644 --- a/src/three/EffectComposer.ts +++ b/src/three/EffectComposer.ts @@ -21,18 +21,17 @@ const defaultParams = { * @public */ export class EffectComposer { + // given on construct private scene: Scene; private camera: PerspectiveCamera; private renderer: WebGLRenderer; - - private varCam = new PerspectiveCamera(); - - private viewSize: Vector2; - private globalPrecision: string; - private _previousFrameTime = Date.now(); + // runtime + private viewSize: Vector2; + private previousFrame = Date.now(); + // target & origin buffers private defaultTarget: WebGLRenderTarget; private renderWrite: WebGLRenderTarget; @@ -41,11 +40,12 @@ export class EffectComposer { private renderRead: WebGLRenderTarget; private readBuffer: WebGLRenderTarget; + // render passes private normPass: RenderPass; private xrPass: RenderPass; + private xrCam = new PerspectiveCamera(); - // render by default - public enabled: boolean = true; + // public public passes: BasePass[] = []; @@ -55,15 +55,16 @@ export class EffectComposer { * @param {PerspectiveCamera} camera * @param {WebGLRenderer} renderer * @param {string} globalPrec + * @param {Color} clearCol * @param {WebGLRenderTarget} renderTarget */ - constructor(scene: Scene, camera: PerspectiveCamera, renderer: WebGLRenderer, globalPrec: string = 'mediump', renderTarget?: WebGLRenderTarget) { + constructor(scene: Scene, camera: PerspectiveCamera, renderer: WebGLRenderer, globalPrec: string = 'mediump', clearCol?: any, renderTarget?: WebGLRenderTarget) { this.scene = scene; this.camera = camera; this.renderer = renderer; this.viewSize = renderer.getSize(new Vector2()); - this.varCam = new PerspectiveCamera(camera.fov, camera.aspect, camera.near, camera.far); + this.xrCam = new PerspectiveCamera(camera.fov, camera.aspect, camera.near, camera.far); // use a new default render target if none is given this.defaultTarget = new WebGLRenderTarget(this.viewSize.width, this.viewSize.height, defaultParams); @@ -71,7 +72,7 @@ export class EffectComposer { if (renderTarget === undefined) { renderTarget = this.defaultTarget.clone(); - renderTarget.texture.name = 'EffectComposer.wt'; + renderTarget.texture.name = 'EffectComposer.rt'; } // set write buffer for shader pass rendering @@ -80,15 +81,23 @@ export class EffectComposer { // set input buffer for shader pass rendering this.renderRead = renderTarget.clone(); - this.renderRead.texture.name = 'EffectComposer.rt'; + this.renderRead.texture.name = 'EffectComposer.rr'; this.readBuffer = this.renderRead; this.passes = []; - this._previousFrameTime = Date.now(); + this.previousFrame = Date.now(); this.globalPrecision = globalPrec; - this.normPass = new RenderPass(scene, camera, null, 0x000000, 1); - this.xrPass = new RenderPass(scene, this.varCam, null, 0x000000, 1); + this.normPass = new RenderPass(scene, camera, null, clearCol, 1); + this.xrPass = new RenderPass(scene, this.xrCam, null, clearCol, 1); + } + + /** + * Precompile all shaders... + */ + public precompile(): void { + this.renderer.compile(this.scene, this.camera); + this.passes.forEach((pass) => pass.prepare(this.renderer)); } /** @@ -136,9 +145,9 @@ export class EffectComposer { // deltaTime value is in seconds const dn = performance.now(); if (deltaTime === undefined) { - deltaTime = (dn - this._previousFrameTime) * 0.001; + deltaTime = (dn - this.previousFrame) * 0.001; } - this._previousFrameTime = dn; + this.previousFrame = dn; const size = new Vector2(); this.renderer.getSize( size ); const currentRenderTarget = this.renderer.getRenderTarget(); @@ -171,17 +180,19 @@ export class EffectComposer { // position const varPos = view.transform.position; - this.varCam.position.set(camPos.x + varPos.x, + this.xrCam.position.set(camPos.x + varPos.x, camPos.y + (varPos.y - 1.6), camPos.z + varPos.z); // orientation const vo = view.transform.orientation; - this.varCam.setRotationFromQuaternion(new Quaternion(vo.x, vo.y, vo.z, vo.w)); + this.xrCam.setRotationFromQuaternion(new Quaternion(vo.x, vo.y, vo.z, vo.w)); // matrix - this.varCam.projectionMatrix.fromArray(view.projectionMatrix); - this.varCam.near = this.camera.near; + this.xrCam.projectionMatrix.fromArray(view.projectionMatrix); + this.xrCam.near = this.camera.near; + this.xrCam.far = this.camera.far; + this.xrCam.updateProjectionMatrix(); // render const offX = viewSize * i; diff --git a/src/three/pass/BasePass.ts b/src/three/pass/BasePass.ts index 167ebab..06ab87b 100644 --- a/src/three/pass/BasePass.ts +++ b/src/three/pass/BasePass.ts @@ -1,3 +1,5 @@ +import {WebGLRenderer} from 'three'; + /** * @author alteredq / http://alteredqualia.com/ * @author hexxone / https://hexx.one @@ -19,6 +21,8 @@ export interface BasePass { // if set to true, the pass clears its buffer before rendering clear: boolean; // = false; + prepare(renderer: WebGLRenderer); + dispose(); setSize(width: number, height: number); diff --git a/src/three/pass/FullScreenHelper.ts b/src/three/pass/FullScreenHelper.ts index 7fb8430..dfe7329 100644 --- a/src/three/pass/FullScreenHelper.ts +++ b/src/three/pass/FullScreenHelper.ts @@ -29,6 +29,14 @@ export class FullScreenHelper { this.mesh = new Mesh(this.geometry, material); } + /** + * precompile shader + * @param {WebGLRenderer} renderer + */ + public prepare(renderer: WebGLRenderer) { + renderer.compile(this.mesh, this.camera); + } + /** * Change mesh material * @param {Material} mat diff --git a/src/three/pass/RenderPass.ts b/src/three/pass/RenderPass.ts index c1b7d57..b027b28 100644 --- a/src/three/pass/RenderPass.ts +++ b/src/three/pass/RenderPass.ts @@ -4,7 +4,7 @@ * @author hexxone / https://hexx.one */ -import {Camera, Color, Material, Scene} from 'three'; +import {Camera, Color, Material, Scene, WebGLRenderer} from 'three'; import {BasePass} from './BasePass'; /** @@ -44,6 +44,14 @@ export class RenderPass implements BasePass { this.clearAlpha = (clearAlpha !== undefined) ? clearAlpha : 0; } + /** + * precompile shader + * @param {WebGLRenderer} renderer + */ + public prepare(renderer: WebGLRenderer) { + renderer.compile(this.scene, this.camera); + } + /** * Destroy shader */ diff --git a/src/three/pass/ShaderPass.ts b/src/three/pass/ShaderPass.ts index 791f335..06ba861 100644 --- a/src/three/pass/ShaderPass.ts +++ b/src/three/pass/ShaderPass.ts @@ -52,6 +52,14 @@ export class ShaderPass implements BasePass { this.fsQuad = new FullScreenHelper(this.material); } + /** + * precompile shader + * @param {WebGLRenderer} renderer + */ + public prepare(renderer: WebGLRenderer) { + this.fsQuad.prepare(renderer); + } + /** * Destroy Pass * @public diff --git a/src/three/pass/UnrealBloomPass.ts b/src/three/pass/UnrealBloomPass.ts index efaec68..98f568b 100644 --- a/src/three/pass/UnrealBloomPass.ts +++ b/src/three/pass/UnrealBloomPass.ts @@ -148,6 +148,14 @@ export class UnrealBloomPass implements BasePass { }); } + /** + * precompile shader + * @param {WebGLRenderer} renderer + */ + public prepare(renderer: WebGLRenderer) { + this.fsQuad.prepare(renderer); + } + /** * Destroy shader */ diff --git a/src/three/shader/LuminosityHighPassShader.ts b/src/three/shader/LuminosityHighPassShader.ts index c88e7a7..1eeb1da 100644 --- a/src/three/shader/LuminosityHighPassShader.ts +++ b/src/three/shader/LuminosityHighPassShader.ts @@ -27,7 +27,7 @@ export class LuminosityHighPassShader implements BaseShader { tDiffuse: {value: null}, luminosityThreshold: {value: 1.0}, smoothWidth: {value: 1.0}, - defaultColor: {value: new Color(0x000000)}, + defaultColor: {value: new Color(0x000000)}, // @TODO might need to set to BG color? defaultOpacity: {value: 0.0}, }; diff --git a/src/wasc-worker b/src/wasc-worker index ed5803c..d8e176a 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit ed5803c3c0ced0fdd5f3cc8d6afecb5024adeef8 +Subproject commit d8e176a161351f3fced7141d73f2fe9ff31f877d From 5e9dae43a0dcbbf12ceae080bc7e0f27bf54a0c7 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Wed, 23 Jun 2021 16:51:43 +0200 Subject: [PATCH 32/76] RenamerPlugin; refactoring --- index.ts | 3 +- src/Smallog.ts | 86 ++++++++++---------- src/WEICUE.ts | 1 + src/WEWA.ts | 27 ++---- src/WarnHelper.ts | 1 + src/offline/OfflineHelper.ts | 6 +- src/renamer/RenamerPlugin.js | 131 ++++++++++++++++++++++++++++++ src/three/XRHelper.ts | 7 +- src/three/pass/UnrealBloomPass.ts | 66 +++++++-------- src/wasc-worker | 2 +- 10 files changed, 229 insertions(+), 101 deletions(-) create mode 100644 src/renamer/RenamerPlugin.js diff --git a/index.ts b/index.ts index d0d3f32..a705875 100644 --- a/index.ts +++ b/index.ts @@ -12,13 +12,14 @@ export * from './src/CSettings'; export * from './src/FPSta'; export * from './src/ReloadHelper'; -export * from './src/Smallog'; export * from './src/Util'; export * from './src/WarnHelper'; export * from './src/WEAS'; export * from './src/WEICUE'; export * from './src/WEWA'; +export * from './src/Smallog'; + export * from './src/offline/OfflineHelper'; export * from './src/three'; diff --git a/src/Smallog.ts b/src/Smallog.ts index 6ec3428..cd04049 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -11,29 +11,7 @@ /* eslint-disable no-unused-vars */ /** -* trace exception calls -* @param {string} def error message -* @param {number} depth which call to pick -* @return {string} -* @ignore -*/ -function traceCall(def: string, depth: number = 3) { - try { - throw new Error('TraceCall()'); - } catch (e) { - // Examine e.stack here - if (e.stack) { - const splt = e.stack.split(/\n/); - let trim = splt[depth].trim(); - if (trim.indexOf('at ') == 0) trim = trim.substring(3); - if (splt.length > depth) return '[' + trim + '] '; - } - } - return def; -} - -/** -* @see {Smallog} +* @see {Smalog} * @public */ export enum LogLevel { @@ -55,33 +33,55 @@ export enum LogLevel { * Small logging util, with name/time prefix & log levels * @public */ -export module Smallog { - const logLevel: LogLevel = LogLevel.Info; - let preFix: string = '[Smallog] '; - let printTime: boolean = false; +class Smalog { + logLevel: LogLevel = LogLevel.Info; + preFix: string = '[Smallog] '; + printTime: boolean = false; + + /** + * trace exception calls + * @param {string} def error message + * @param {number} depth which call to pick + * @return {string} + * @ignore + */ + traceCall(def: string, depth: number = 3) { + try { + throw new Error('TraceCall()'); + } catch (e) { + // Examine e.stack here + if (e.stack) { + const splt = e.stack.split(/\n/); + let trim = splt[depth].trim(); + if (trim.indexOf('at ') == 0) trim = trim.substring(3); + if (splt.length > depth) return '[' + trim + '] '; + } + } + return def; + } /** * get logging output level * @return {LogLevel} current */ - export function getLevel() { - return logLevel; + getLevel() { + return this.logLevel; } /** * set logging prefix * @param {string} pre */ - export function setPrefix(pre: string) { - preFix = pre; + setPrefix(pre: string) { + this.preFix = pre; } /** * set time prefix * @param {boolean} print */ - export function setPrintTime(print: boolean) { - printTime = print; + setPrintTime(print: boolean) { + this.printTime = print; } /** @@ -89,8 +89,8 @@ export module Smallog { * @param {string} msg log * @param {string} hdr overwrite header */ - export function error(msg: string, hdr: string = preFix) { - if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + error(msg: string, hdr: string = this.preFix) { + if (this.printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; console.error(hdr + msg); } @@ -99,10 +99,10 @@ export module Smallog { * @param {string} msg log * @param {string} hdr overwrite header */ - export function info(msg: string, hdr: string = preFix) { - if (logLevel >= 1) { - if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; - if (logLevel >= 2) hdr = traceCall(hdr); + info(msg: string, hdr: string = this.preFix) { + if (this.logLevel >= 1) { + if (this.printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + if (this.logLevel >= 2) hdr = this.traceCall(hdr); console.info(hdr + msg); } } @@ -112,10 +112,12 @@ export module Smallog { * @param {string} msg log * @param {string} hdr overwrite header */ - export function debug(msg: string, hdr: string = preFix) { - if (logLevel >= 2) { - if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + debug(msg: string, hdr: string = this.preFix) { + if (this.logLevel >= 2) { + if (this.printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; console.debug(hdr + msg); } } } + +export const Smallog = new Smalog(); diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 2fc87b6..dfc8a20 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -164,6 +164,7 @@ export class WEICUE extends CComponent { const imgg = document.createElement('img'); imgg.id = 'icuelogo'; imgg.setAttribute('src', IMG_SRC); + imgg.setAttribute('alt', 'ICUE Icon'); // make text holder this.texter = document.createElement('div'); this.texter.id = 'icuetext'; diff --git a/src/WEWA.ts b/src/WEWA.ts index 66ddb59..04d63d7 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -9,9 +9,8 @@ import {waitReady} from './Util'; import {Smallog} from './Smallog'; -import {register, reset} from './offline/OfflineHelper'; -import {CC} from 'cookieconsent'; -import {myFetch} from './wasc-worker/WascUtil'; +import {OfflineHelper} from './offline/OfflineHelper'; +import {WascUtil} from './wasc-worker/WascUtil'; const LogHead = '[WEWWA] '; const DefLang = 'de-de'; @@ -58,8 +57,6 @@ const DefLang = 'de-de'; * - react to changes made in the ui and update them in the wallpaper *
      * - save changes made in the ui to localStorage -*
      -* - Annoying Cookie Popup (Thanks DSGVO) * * * @todo @@ -113,20 +110,8 @@ export class WEWWA { // intialize when ready waitReady().then(() => { - if (CC) {/* This tells the compiler to include CookieConsent at this point. */} - - // Thanks DSGVO... - window['cookieconsent'].initialise({ - palette: { - popup: {background: '#000'}, - button: {background: '#f1d600'}, - }, - position: 'bottom-left', - theme: 'edgeless', - }); - // make the website available offline using service worker - register(document.title.replace(' ', '')).then(() => { + OfflineHelper.register(document.title.replace(' ', '')).then(() => { // continue initializing finished(); this.init(); @@ -143,7 +128,7 @@ export class WEWWA { * @ignore */ private init() { - myFetch('project.json', 'json').then((proj) => { + WascUtil.myFetch('project.json', 'json').then((proj) => { if (proj.type != 'web') { Smallog.error('Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...', LogHead); return; @@ -489,7 +474,7 @@ export class WEWWA { if (!window.confirm('This action will clear ALL local data!\r\n\r\nAre you sure?')) { return; } - reset().then(() => { + OfflineHelper.reset().then(() => { localStorage.clear(); location = location; }); @@ -881,7 +866,7 @@ export class WEWWA { * @ignore */ private loadXHRSaveLocal(url, resCall) { - myFetch(url, 'blob').then((resp) => { + WascUtil.myFetch(url, 'blob').then((resp) => { // Read out file contents as a Data URL const fReader = new FileReader(); // onload needed since Google Chrome doesn't support addEventListener for FileReader diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 2649671..e6a1407 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -80,6 +80,7 @@ export class WarnHelper extends CComponent { this.element = document.createElement('img'); this.element.id = ELM_ID; this.element.setAttribute('src', IMG_SRC); + this.element.setAttribute('alt', 'Seizure Warning'); document.body.append(this.element); } diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index 24dd842..5652ce3 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -49,7 +49,7 @@ function DontRemove() { * @param {string} oFile * @return {Promise} */ -export function register(name: string, worker = 'Offline.worker.js', oFile = 'offlinefiles.json') { +function register(name: string, worker: string = 'Offline.worker.js', oFile: string = 'offlinefiles.json'): Promise { return new Promise(async (resolve) => { if ('serviceWorker' in navigator) { const workerPath = `${worker}?name=${name}&jsonPath=${oFile}`; @@ -70,7 +70,7 @@ export function register(name: string, worker = 'Offline.worker.js', oFile = 'of * @return {Promise} finished * @public */ -export async function reset() { +async function reset(): Promise { return new Promise((resolve) => { if ('serviceWorker' in navigator) { navigator.serviceWorker.getRegistrations().then(async (registrations) => { @@ -85,3 +85,5 @@ export async function reset() { } }); } + +export const OfflineHelper = {register, reset}; diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js new file mode 100644 index 0000000..c40bebd --- /dev/null +++ b/src/renamer/RenamerPlugin.js @@ -0,0 +1,131 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +* @ignore +*/ + +const {RawSource} = require('webpack-sources'); + +const validate = require('schema-utils'); +const pluginName = 'RenamerPlugin'; + +/** +* schema for options object +* @see {RenamerPlugin} +*/ +const offlineSchema = { + type: 'object', + properties: { + regex: { + type: 'object', + }, + }, +}; + +/** +* Bruh +*/ +class RenamerPlugin { + options = {}; + + // mapped name list: {key,value} + nameMap = []; + // saved character count + savedChars = 0; + + /** + * Intializes the plugin in the webpack build process + * @param {offliineSchema} options + */ + constructor(options = {}) { + validate.validate(offlineSchema, options); + this.options = options; + } + + /** + * Get a random variable name, which does not exist in src and mappings + * @param {string} src + * @return {string} + */ + getRandomName(src) { + const gen = 'hx' + Math.random().toString(36).substr(2, 3) + Math.floor(10 + Math.random() * 89); + let exist = (src || '').indexOf(gen) >= 0; + this.nameMap.forEach((mping) => { + if (mping.key === gen) exist = true; + }); + return exist ? this.getRandomName() : gen; + } + + /** + * Regex replace "match" function + * @param {string} source + * @param {string} match + * @return {string} + */ + replaceMatch(source, match) { + let fnd = null; + // check if this exact name is already mapped + this.nameMap.forEach((mping) => { + if (mping.key === match) fnd = mping.val; + }); + if (fnd) return fnd; + // get & add a new random variable name + fnd = this.getRandomName(source); + this.nameMap.push({key: match, val: fnd}); + return fnd; + } + + /** + * replace all regex matches with random variable names + * @param {string} source + * @return {string} + */ + processString(source) { + return source.replace(this.options.regex, (match) => this.replaceMatch(source, match)); // .replaceAll('const ', 'var '); + } + + /** + * Hook into the compilation process, + * Replace regex matches with random strings + * @param {Webpack.compiler} compiler object from webpack + */ + apply(compiler) { + compiler.hooks.emit.tap(pluginName, (compilation) => { + try { + console.info('[' + pluginName + '] Using Regex: ' + this.options.regex); + + // process all compiled .js files + for (const assetFile in compilation.assets) { + if (!assetFile || !assetFile.endsWith('.js')) continue; + console.info('[' + pluginName + '] Processing: ' + assetFile); + + // get the processed asset object / source + const asset = compilation.getAsset(assetFile); + const source = asset.source._value; + const processed = this.processString(source); + + // if anything changed, update the processed asset + if (source != processed) { + compilation.updateAsset(assetFile, new RawSource(processed)); + // calculate saved memory + this.savedChars += (source.length - processed.length); + } + } + + // finish up + console.info('[' + pluginName + '] Replaced: ', this.nameMap); + console.info('[' + pluginName + '] Saved: ' + this.savedChars + ' chars'); + } catch (error) { + console.info('[' + pluginName + '] Replace error: ', error); + } + }); + } +} + +module.exports = RenamerPlugin; + + diff --git a/src/three/XRHelper.ts b/src/three/XRHelper.ts index fc9dac1..37698cb 100644 --- a/src/three/XRHelper.ts +++ b/src/three/XRHelper.ts @@ -7,6 +7,7 @@ import {Navigator, XRSession} from 'three'; import {CComponent} from '../CComponent'; import {CSettings} from '../CSettings'; +import {waitReady} from '../Util'; /** * XR Settings @@ -34,8 +35,10 @@ export class XRHelper extends CComponent { */ constructor() { super(); - this.nav = navigator as Navigator; - this.createBtn(); + waitReady().then(() => { + this.nav = navigator as Navigator; + this.createBtn(); + }); } /** diff --git a/src/three/pass/UnrealBloomPass.ts b/src/three/pass/UnrealBloomPass.ts index 98f568b..348d0f5 100644 --- a/src/three/pass/UnrealBloomPass.ts +++ b/src/three/pass/UnrealBloomPass.ts @@ -16,38 +16,40 @@ import {LuminosityHighPassShader} from '../shader/LuminosityHighPassShader'; * @public */ export class UnrealBloomPass implements BasePass { - name = 'UnrealBloom'; - strength = null; - radius = null; - resolution = null; - threshold = null; - renderTargetBright = null; - highPassUniforms = null; + public name = 'UnrealBloom'; + public enabled = true; + public needsSwap = false; + public clear = true; + + public oldClearColor = new Color(); + public oldClearAlpha = 1; + public clearColor = new Color(0, 0, 0); + + private resolution = null; + private strength = null; + private radius = null; + private threshold = null; + private renderTargetBright = null; + private highPassUniforms = null; // create color only once here, reuse it later inside the render function - clear = true; - clearColor = new Color(0, 0, 0); - renderTargetsHorizontal: WebGLRenderTarget[] = []; - renderTargetsVertical: WebGLRenderTarget[] = []; - nMips = 5; - - separableBlurMaterials: ShaderMaterial[] = []; - materialHighPassFilter: ShaderMaterial = null; - compositeMaterial: ShaderMaterial = null; - materialCopy: ShaderMaterial = null; - bloomTintColors = null; - copyUniforms = null; - enabled = true; - needsSwap = false; - - oldClearColor = new Color(); - oldClearAlpha = 1; - - basic = new MeshBasicMaterial(); - fsQuad = new FullScreenHelper(null); - - BlurDirectionX = new Vector2(1.0, 0.0); - BlurDirectionY = new Vector2(0.0, 1.0); + + private renderTargetsHorizontal: WebGLRenderTarget[] = []; + private renderTargetsVertical: WebGLRenderTarget[] = []; + private nMips = 5; + + private separableBlurMaterials: ShaderMaterial[] = []; + private materialHighPassFilter: ShaderMaterial = null; + private compositeMaterial: ShaderMaterial = null; + private materialCopy: ShaderMaterial = null; + private bloomTintColors = null; + private copyUniforms = null; + + private basic = new MeshBasicMaterial(); + private fsQuad = new FullScreenHelper(null); + + private blurDirX = new Vector2(1.0, 0.0); + private blurDirY = new Vector2(0.0, 1.0); /** * Construct bloom shader @@ -237,13 +239,13 @@ export class UnrealBloomPass implements BasePass { this.fsQuad.setMaterial(this.separableBlurMaterials[i]); this.separableBlurMaterials[i].uniforms['colorTexture'].value = inputRenderTarget.texture; - this.separableBlurMaterials[i].uniforms['direction'].value = this.BlurDirectionX; + this.separableBlurMaterials[i].uniforms['direction'].value = this.blurDirX; renderer.setRenderTarget(this.renderTargetsHorizontal[i]); renderer.clear(); this.fsQuad.render(renderer); this.separableBlurMaterials[i].uniforms['colorTexture'].value = this.renderTargetsHorizontal[i].texture; - this.separableBlurMaterials[i].uniforms['direction'].value = this.BlurDirectionY; + this.separableBlurMaterials[i].uniforms['direction'].value = this.blurDirY; renderer.setRenderTarget(this.renderTargetsVertical[i]); renderer.clear(); this.fsQuad.render(renderer); diff --git a/src/wasc-worker b/src/wasc-worker index d8e176a..fd75616 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit d8e176a161351f3fced7141d73f2fe9ff31f877d +Subproject commit fd756169015e1aaf561923e04497ef9cbafa6fbd From f5947c5a7daf612b4fae5a3299715f2a74ae6aaa Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Fri, 2 Jul 2021 16:00:04 +0200 Subject: [PATCH 33/76] Update Docs --- docs/BasePass.html | 4 +- docs/BaseShader.html | 2 +- docs/BlendShader.html | 2 +- docs/BlurShader.html | 2 +- docs/CComponent.html | 2 +- docs/CComponent.ts.html | 2 +- docs/CSettings.html | 2 +- docs/CSettings.ts.html | 2 +- docs/CUESettings.html | 2 +- docs/ChromaticShader.html | 2 +- docs/CopyShader.html | 2 +- docs/EffectComposer.html | 144 +- docs/FPSettings.html | 2 +- docs/FPSta.ts.html | 2 +- docs/FPStats.html | 2 +- docs/FXAAShader.html | 2 +- docs/FractalMirrorShader.html | 2 +- docs/FullScreenHelper.html | 156 +- docs/ICUE.d.ts.html | 350 ++++ docs/ICUE.html | 742 ++++++++ docs/LUTShader.html | 2 +- docs/LUTShaderNearest.html | 2 +- docs/LuminosityHighPassShader.html | 2 +- docs/OfflinePlugin.html | 2 +- docs/ReloadHelper.html | 8 +- docs/ReloadHelper.ts.html | 14 +- docs/ReloadSettings.html | 2 +- docs/RenamerPlugin.html | 999 +++++++++++ docs/RenderPass.html | 156 +- docs/ShaderPass.html | 154 +- docs/Smallog.ts.html | 88 +- docs/Smalog.html | 1195 +++++++++++++ docs/UnrealBloomPass.html | 160 +- docs/Util.ts.html | 53 +- docs/WEAS.html | 14 +- docs/WEAS.ts.html | 23 +- docs/WEASettings.html | 4 +- docs/WEAudio.html | 4 +- docs/WEICUE.html | 6 +- docs/WEICUE.ts.html | 50 +- docs/WEWA.ts.html | 29 +- docs/WEWWA.html | 16 +- docs/WarnHelper.html | 8 +- docs/WarnHelper.ts.html | 5 +- docs/WarnSettings.html | 2 +- docs/XRHelper.html | 10 +- docs/XRSettings.html | 4 +- docs/global.html | 1505 ++++++++++++++++- docs/index.html | 2 +- docs/offline_OfflineHelper.ts.html | 8 +- docs/offline_OfflinePlugin.js.html | 2 +- docs/renamer_RenamerPlugin.js.html | 247 +++ docs/three_EffectComposer.ts.html | 55 +- docs/three_XRHelper.ts.html | 9 +- docs/three_pass_BasePass.ts.html | 8 +- docs/three_pass_FullScreenHelper.ts.html | 10 +- docs/three_pass_RenderPass.ts.html | 12 +- docs/three_pass_ShaderPass.ts.html | 10 +- docs/three_pass_UnrealBloomPass.ts.html | 76 +- docs/three_shader_BaseShader.ts.html | 2 +- docs/three_shader_BlendShader.ts.html | 2 +- docs/three_shader_BlurShader.ts.html | 2 +- docs/three_shader_ChromaticShader.ts.html | 2 +- docs/three_shader_CopyShader.ts.html | 2 +- docs/three_shader_FXAAShader.ts.html | 2 +- docs/three_shader_FractalMirrorShader.ts.html | 2 +- docs/three_shader_LUTShader.ts.html | 2 +- docs/three_shader_LUTShaderNearest.ts.html | 2 +- ...ee_shader_LuminosityHighPassShader.ts.html | 4 +- src/ReloadHelper.ts | 12 +- 70 files changed, 6102 insertions(+), 314 deletions(-) create mode 100644 docs/ICUE.d.ts.html create mode 100644 docs/ICUE.html create mode 100644 docs/RenamerPlugin.html create mode 100644 docs/Smalog.html create mode 100644 docs/renamer_RenamerPlugin.js.html diff --git a/docs/BasePass.html b/docs/BasePass.html index 03341cd..561fcf3 100644 --- a/docs/BasePass.html +++ b/docs/BasePass.html @@ -66,7 +66,7 @@ @@ -142,7 +142,7 @@

      BasePass

      View Source - three/pass/BasePass.ts, line 4 + three/pass/BasePass.ts, line 5

      diff --git a/docs/BaseShader.html b/docs/BaseShader.html index 0e5c712..289a9d6 100644 --- a/docs/BaseShader.html +++ b/docs/BaseShader.html @@ -66,7 +66,7 @@ diff --git a/docs/BlendShader.html b/docs/BlendShader.html index 120fdab..30764d1 100644 --- a/docs/BlendShader.html +++ b/docs/BlendShader.html @@ -66,7 +66,7 @@ diff --git a/docs/BlurShader.html b/docs/BlurShader.html index 606b2f0..d9a2231 100644 --- a/docs/BlurShader.html +++ b/docs/BlurShader.html @@ -66,7 +66,7 @@ diff --git a/docs/CComponent.html b/docs/CComponent.html index 30f0d8e..a496fa6 100644 --- a/docs/CComponent.html +++ b/docs/CComponent.html @@ -66,7 +66,7 @@ diff --git a/docs/CComponent.ts.html b/docs/CComponent.ts.html index 5dfe845..d397f7c 100644 --- a/docs/CComponent.ts.html +++ b/docs/CComponent.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/CSettings.html b/docs/CSettings.html index a384357..ab17538 100644 --- a/docs/CSettings.html +++ b/docs/CSettings.html @@ -66,7 +66,7 @@ diff --git a/docs/CSettings.ts.html b/docs/CSettings.ts.html index a4b2708..0a53d48 100644 --- a/docs/CSettings.ts.html +++ b/docs/CSettings.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/CUESettings.html b/docs/CUESettings.html index d3c70e5..4f39c8e 100644 --- a/docs/CUESettings.html +++ b/docs/CUESettings.html @@ -66,7 +66,7 @@ diff --git a/docs/ChromaticShader.html b/docs/ChromaticShader.html index 466b926..218326e 100644 --- a/docs/ChromaticShader.html +++ b/docs/ChromaticShader.html @@ -66,7 +66,7 @@ diff --git a/docs/CopyShader.html b/docs/CopyShader.html index c7c4bd6..79698eb 100644 --- a/docs/CopyShader.html +++ b/docs/CopyShader.html @@ -66,7 +66,7 @@ diff --git a/docs/EffectComposer.html b/docs/EffectComposer.html index 10c2f55..6059b33 100644 --- a/docs/EffectComposer.html +++ b/docs/EffectComposer.html @@ -66,7 +66,7 @@ @@ -85,7 +85,7 @@

      EffectComposer

      -

      EffectComposer(scene, camera, renderer, globalPrec, renderTarget)

      +

      EffectComposer(scene, camera, renderer, globalPrec, clearCol, renderTarget)

      render shader chain
      @@ -111,7 +111,7 @@

      - new EffectComposer(scene, camera, renderer, globalPrec, renderTarget) + new EffectComposer(scene, camera, renderer, globalPrec, clearCol, renderTarget)

      @@ -276,6 +276,35 @@
      Parameters:
      + + + clearCol + + + + + +Color + + + + + + + + + + + + + + + + + + + + renderTarget @@ -526,7 +555,7 @@
      Parameters:

      View Source - three/EffectComposer.ts, line 65 + three/EffectComposer.ts, line 72

      @@ -699,7 +728,7 @@
      Parameters:

      View Source - three/EffectComposer.ts, line 76 + three/EffectComposer.ts, line 83

      @@ -847,7 +876,7 @@
      Parameters:

      View Source - three/EffectComposer.ts, line 86 + three/EffectComposer.ts, line 93

      @@ -895,6 +924,103 @@
      Parameters:
      + + + +
      + + + +

      + # + + + + precompile() + + +

      + + + + +
      + Precompile all shaders... +
      + + + + + + + + + + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + three/EffectComposer.ts, line 63 + +

      + +
      + + + + + + + + + + + + + + + + + + + +
      @@ -1043,7 +1169,7 @@
      Parameters:

      View Source - three/EffectComposer.ts, line 99 + three/EffectComposer.ts, line 106

      @@ -1191,7 +1317,7 @@
      Parameters:

      View Source - three/EffectComposer.ts, line 182 + three/EffectComposer.ts, line 191

      @@ -1364,7 +1490,7 @@
      Parameters:

      View Source - three/EffectComposer.ts, line 203 + three/EffectComposer.ts, line 212

      diff --git a/docs/FPSettings.html b/docs/FPSettings.html index cce98e7..d717bea 100644 --- a/docs/FPSettings.html +++ b/docs/FPSettings.html @@ -66,7 +66,7 @@ diff --git a/docs/FPSta.ts.html b/docs/FPSta.ts.html index 19ddecf..c4555c9 100644 --- a/docs/FPSta.ts.html +++ b/docs/FPSta.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/FPStats.html b/docs/FPStats.html index afff2e6..d2d2fe1 100644 --- a/docs/FPStats.html +++ b/docs/FPStats.html @@ -66,7 +66,7 @@ diff --git a/docs/FXAAShader.html b/docs/FXAAShader.html index 383fef7..f74bba8 100644 --- a/docs/FXAAShader.html +++ b/docs/FXAAShader.html @@ -66,7 +66,7 @@ diff --git a/docs/FractalMirrorShader.html b/docs/FractalMirrorShader.html index b73e251..167f557 100644 --- a/docs/FractalMirrorShader.html +++ b/docs/FractalMirrorShader.html @@ -66,7 +66,7 @@ diff --git a/docs/FullScreenHelper.html b/docs/FullScreenHelper.html index 8526097..19bece7 100644 --- a/docs/FullScreenHelper.html +++ b/docs/FullScreenHelper.html @@ -66,7 +66,7 @@ @@ -351,7 +351,155 @@

      View Source - three/pass/FullScreenHelper.ts, line 45 + three/pass/FullScreenHelper.ts, line 52 + +

      + + + + + + + + + + + + + + + + + + + + + + + + + +
      + + + +

      + # + + + + prepare(renderer) + + +

      + + + + +
      + precompile shader +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      renderer + + +WebGLRenderer + + + +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + three/pass/FullScreenHelper.ts, line 32

      @@ -499,7 +647,7 @@
      Parameters:

      View Source - three/pass/FullScreenHelper.ts, line 39 + three/pass/FullScreenHelper.ts, line 46

      @@ -647,7 +795,7 @@
      Parameters:

      View Source - three/pass/FullScreenHelper.ts, line 32 + three/pass/FullScreenHelper.ts, line 39

      diff --git a/docs/ICUE.d.ts.html b/docs/ICUE.d.ts.html new file mode 100644 index 0000000..4a64948 --- /dev/null +++ b/docs/ICUE.d.ts.html @@ -0,0 +1,350 @@ + + + + + + + + + + ICUE.d.ts + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      ICUE.d.ts

      +
      + + + + + +
      +
      +
      /**
      +* @author 'Andrew' / https://github.com/profezzional
      +*
      +* Types for Wallpaper Engine integation for CUE SDK 3.0.+
      +*
      +* @see https://wallpaper-engine.fandom.com/wiki/Web_Wallpaper_iCUE_Reference for reference
      +* @see http://forum.corsair.com/v3/showthread.php?t=179027 for the latest SDK version
      +* @see https://github.com/profezzional/iCUE-wallpaper-engine for orginal source
      +*/
      +
      +/**
      +* Contains information about SDK and CUE versions.
      +*/
      +type ProtocolDetails = {
      +	/**
      +	* Boolean value that specifies if there were breaking changes between version of protocol implemented by server and client.
      +	*/
      +	breakingChanges: boolean,
      +
      +	/**
      +	* Integer number that specifies version of protocol that is implemented by current SDK.
      +	* Numbering starts from 1. Always contains valid value even if there was no CUE found.
      +	*/
      +	sdkProtocolVersion: number,
      +
      +	/**
      +	* String containing version of SDK (like "1.0.0.1"). Always contains valid value even if there was no CUE found.
      +	*/
      +	sdkVersion: string,
      +
      +	/**
      +	* Integer number that specifies version of protocol that is implemented by CUE.
      +	* Numbering starts from 1. If CUE was not found then this value will be 0.
      +	*/
      +	serverProtocolVersion: number,
      +
      +	/**
      +	* String containing version of CUE (like "1.0.0.1") or NULL if CUE was not found.
      +	*/
      +	serverVersion: number
      +};
      +
      +/**
      +* Contains list of available device types
      +*/
      +type DeviceType = 'CDT_Keyboard' | 'CDT_Mouse' | 'CDT_Headset' | 'CDT_Mousemat' | 'CDT_HeadsetStand' | 'CDT_CommanderPro' | 'CDT_LightingNodePro';
      +
      +/**
      +* Valid values for keyboard physical layouts.
      +*/
      +type KeyboardPhysicalLayout = 'CPL_US' | 'CPL_UK' | 'CPL_JP' | 'CPL_KR' | 'CPL_BR';
      +
      +/**
      +* Valid values for mouse physical layouts, number represents configurable mouse LEDs.
      +*/
      +type MousePhysicalLayout = 'CPL_Zones1' | 'CPL_Zones2' | 'CPL_Zones3' | 'CPL_Zones4';
      +
      +/**
      +* Contains list of available physical layouts for keyboards and mice.
      +*/
      +type PhysicalLayout = KeyboardPhysicalLayout | MousePhysicalLayout | 'CPL_Invalid';
      +
      +/**
      +* Contains list of available logical layouts for keyboards.
      +*/
      +type KeyboardLogicalLayout = 'CLL_US_Int' | 'CLL_NA' | 'CLL_EU' | 'CLL_UK' | 'CLL_BE' | 'CLL_BR' | 'CLL_CH' | 'CLL_CN' | 'CLL_DE'
      +							| 'CLL_ES' | 'CLL_FR' | 'CLL_IT' | 'CLL_ND' | 'CLL_RU4' | 'CLL_JP' | 'CLL_KR' | 'CLL_TW' | 'CLL_MEX';
      +
      +/**
      +* Contains list of available logical layouts for keyboards.
      +*/
      +type LogicalLayout = KeyboardLogicalLayout | 'CLL_Invalid';
      +
      +/**
      +* Contains information about device.
      +*/
      +type DeviceInfo = {
      +	/**
      +	* ICUE Device ID (not set by default)
      +	*/
      +	id: number,
      +
      +	/**
      +	* Enum describing device type.
      +	*/
      +	type: DeviceType,
      +
      +	/**
      +	* Device model (like "K95RGB").
      +	*/
      +	model: string,
      +
      +	/**
      +	* Enum describing physical layout of the keyboard or mouse.
      +	* If device is neither keyboard nor mouse then value is "CPL_Invalid".
      +	*/
      +	physicalLayout: PhysicalLayout;
      +
      +	/**
      +	* Enum describing logical layout of the keyboard as set in CUE settings.
      +	* If device is not keyboard then value is "CLL_Invalid".
      +	*/
      +	logicalLayout: LogicalLayout;
      +
      +	/**
      +	* Number of controllable LEDs on the device.
      +	*/
      +	ledCount: number;
      +
      +	/**
      +	* Led positions on the device (not set by default)
      +	*/
      +	leds: LedPosition[];
      +
      +	/**
      +	* Contains list of device capabilities.
      +	* First version of SDK only supports lighting, but future versions may also support other capabilities.
      +	*/
      +	capsMask: { CDC_Lighting: boolean } | { CDC_None: boolean };
      +};
      +
      +/**
      +* Contains led id and position of led rectangle. Most of the keys are rectangular.
      +* In case if key is not rectangular (like Enter in ISO/UK layout) it returns the smallest rectangle that fully contains the key.
      +*/
      +type LedPosition = {
      +	/**
      +	* For keyboards, mousemats and headset stands, height in mm;
      +	* for DIY-devices, height in logical units.
      +	*/
      +	height: number,
      +
      +	/**
      +	* For keyboards, mousemats and headset stands, width in mm;
      +	* for DIY-devices, width in logical units.
      +	*/
      +	width: number,
      +
      +	/**
      +	* For keyboards, mousemats and headset stands, top offset in mm;
      +	* for DIY-devices, top offset in logical units.
      +	*/
      +	top: number,
      +
      +	/**
      +	* For keyboards, mousemats and headset stands, left offset in mm;
      +	* for DIY-devices, left offset in logical units.
      +	*/
      +	left: number,
      +
      +	/**
      +	* Identifier of led.
      +	*/
      +	ledId: number,
      +
      +	/**
      +	* Identifier of led.
      +	*/
      +	ledIdName: string
      +};
      +
      +/** Contains information about led and its color. */
      +type LedColor = {
      +	/**
      +	* Identifier of LED to set.
      +	*/
      +	ledId: number,
      +
      +	/**
      +	* Red brightness [0..255].
      +	*/
      +	r: number,
      +
      +	/**
      +	* Green brightness [0..255].
      +	*/
      +	g: number,
      +
      +	/**
      +	* Blue brightness [0..255].
      +	*/
      +	b: number
      +};
      +
      +/** Main Interface */
      +export interface ICUE {
      +	/**
      +	* Returns current status and version of iCUE SDK.
      +	* @param callback A callback into which the protocol details are passed.
      +	*/
      +	getProtocolDetails(callback: (protocolDetails: ProtocolDetails) => void): void;
      +
      +	/**
      +	* Returns the number of recognized iCUE compatible devices on the system.
      +	* @param callback A callback into which the device count is passed.
      +	*/
      +	getDeviceCount(callback: (count: number) => void): void;
      +
      +	/**
      +	* Returns all information specific to a single device.
      +	* @param deviceIndex The index of the device about which to get info.
      +	* @param callback A callback into which the device info is passed.
      +	*/
      +	getDeviceInfo(deviceIndex: number, callback: (deviceInfo: DeviceInfo) => void): void;
      +
      +	/**
      +	*  Provides list of keyboard, mousemat, headset stand and DIY-devices LEDs with their physical (keyboard, mousemat and headset stand) or logical (DIY-devices) positions.
      +	* @param deviceIndex The index of the device whose LED position to get.
      +	* @param callback A callback into which the LED positions are passed.
      +	*/
      +	getLedPositionsByDeviceIndex(deviceIndex: number, callback: (leds: LedPosition[]) => void): void;
      +
      +	/**
      +	* Set specified leds to some colors.
      +	* The color is retained until changed by successive calls.
      +	* This function does not take logical layout into account, and returns control to the caller immediately.
      +	* @param leds Array containing colors for each LED.
      +	*/
      +	setLedsColorsAsync(leds: LedColor[]): void;
      +
      +	/**
      +	* Updates all LEDs for given devices to one specific color.
      +	* @param deviceIndexOrArray Index or indices of the device(s) whose LEDs to set to the specified color.
      +	* @param ledColor The color to which to change the LEDs of the specified device(s).
      +	*/
      +	setAllLedsColorsAsync(deviceIndexOrArray: number | number[], ledColor: LedColor): void;
      +
      +	/**
      +	* Updates all LEDs for given devices to one specific color.
      +	* @param deviceIndexOrArray Index or indices of the device(s) whose LEDs to set to the specified color.
      +	* @param ledColor The color to which to change the LEDs of the specified device(s).
      +	*/
      +	setLedColorsByImageData(deviceIndexOrArray: number | number[], encodedImageData, width: number, height: number): void;
      +}
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/ICUE.html b/docs/ICUE.html new file mode 100644 index 0000000..7ac2294 --- /dev/null +++ b/docs/ICUE.html @@ -0,0 +1,742 @@ + + + + + + + + ICUE + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Interface

      +

      ICUE

      +
      + + + + + +
      + +
      + +

      ICUE

      + + +
      + +
      +
      + + +
      Main Interface
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + ICUE.d.ts, line 75 + +

      + +
      + + + + +
      + + + + + + + + + + + + + + +
      +

      Members

      +
      + +
      + + + + +void + + + + +

      + # + + + getDeviceCount + + +

      + + + + +
      + Returns the number of recognized iCUE compatible devices on the system. +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + ICUE.d.ts, line 84 + +

      + +
      + + + + + +
      + +
      + + + + +void + + + + +

      + # + + + getDeviceInfo + + +

      + + + + +
      + Returns all information specific to a single device. +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + ICUE.d.ts, line 90 + +

      + +
      + + + + + +
      + +
      + + + + +void + + + + +

      + # + + + getLedPositionsByDeviceIndex + + +

      + + + + +
      + Provides list of keyboard, mousemat, headset stand and DIY-devices LEDs with their physical (keyboard, mousemat and headset stand) or logical (DIY-devices) positions. +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + ICUE.d.ts, line 97 + +

      + +
      + + + + + +
      + +
      + + + + +void + + + + +

      + # + + + getProtocolDetails + + +

      + + + + +
      + Returns current status and version of iCUE SDK. +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + ICUE.d.ts, line 78 + +

      + +
      + + + + + +
      + +
      + + + + +void + + + + +

      + # + + + setAllLedsColorsAsync + + +

      + + + + +
      + Updates all LEDs for given devices to one specific color. +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + ICUE.d.ts, line 112 + +

      + +
      + + + + + +
      + +
      + + + + +void + + + + +

      + # + + + setLedColorsByImageData + + +

      + + + + +
      + Updates all LEDs for given devices to one specific color. +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + ICUE.d.ts, line 119 + +

      + +
      + + + + + +
      + +
      + + + + +void + + + + +

      + # + + + setLedsColorsAsync + + +

      + + + + +
      + Set specified leds to some colors. The color is retained until changed by successive calls. This function does not take logical layout into account, and returns control to the caller immediately. +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + ICUE.d.ts, line 104 + +

      + +
      + + + + + +
      + +
      +
      + + + + + + + +
      + +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + \ No newline at end of file diff --git a/docs/LUTShader.html b/docs/LUTShader.html index 27cd96d..5d27d07 100644 --- a/docs/LUTShader.html +++ b/docs/LUTShader.html @@ -66,7 +66,7 @@
      diff --git a/docs/LUTShaderNearest.html b/docs/LUTShaderNearest.html index 7e926b9..0c8ef94 100644 --- a/docs/LUTShaderNearest.html +++ b/docs/LUTShaderNearest.html @@ -66,7 +66,7 @@ diff --git a/docs/LuminosityHighPassShader.html b/docs/LuminosityHighPassShader.html index f985502..cb42315 100644 --- a/docs/LuminosityHighPassShader.html +++ b/docs/LuminosityHighPassShader.html @@ -66,7 +66,7 @@ diff --git a/docs/OfflinePlugin.html b/docs/OfflinePlugin.html index 3e673e5..4cbe100 100644 --- a/docs/OfflinePlugin.html +++ b/docs/OfflinePlugin.html @@ -66,7 +66,7 @@ diff --git a/docs/ReloadHelper.html b/docs/ReloadHelper.html index fe9ada2..68f3205 100644 --- a/docs/ReloadHelper.html +++ b/docs/ReloadHelper.html @@ -66,7 +66,7 @@ @@ -355,7 +355,7 @@

      View Source - ReloadHelper.ts, line 145 + ReloadHelper.ts, line 146

      @@ -499,7 +499,7 @@

      Parameters:

      View Source - ReloadHelper.ts, line 112 + ReloadHelper.ts, line 113

      @@ -596,7 +596,7 @@

      View Source - ReloadHelper.ts, line 129 + ReloadHelper.ts, line 130

      diff --git a/docs/ReloadHelper.ts.html b/docs/ReloadHelper.ts.html index 59da0ce..fb02ab9 100644 --- a/docs/ReloadHelper.ts.html +++ b/docs/ReloadHelper.ts.html @@ -68,7 +68,7 @@ @@ -143,6 +143,7 @@

      ReloadHelper.ts

      height: 10px; width: 0%; background-color: #989a; + transition: all 0s; } #reload-bar.show { opacity: 1; @@ -202,12 +203,13 @@

      ReloadHelper.ts

      public show(visible: boolean) { const e1 = document.getElementById('reload-bar'); const e2 = document.getElementById('reload-text'); + e1.classList.remove('show'); + e2.classList.remove('show'); if (visible) { - e1.classList.add('show'); - e2.classList.add('show'); - } else { - e1.classList.remove('show'); - e2.classList.remove('show'); + setTimeout(() => { + e1.classList.add('show'); + e2.classList.add('show'); + }, 100); } } diff --git a/docs/ReloadSettings.html b/docs/ReloadSettings.html index 633ccd7..7491eb3 100644 --- a/docs/ReloadSettings.html +++ b/docs/ReloadSettings.html @@ -66,7 +66,7 @@ diff --git a/docs/RenamerPlugin.html b/docs/RenamerPlugin.html new file mode 100644 index 0000000..b611bba --- /dev/null +++ b/docs/RenamerPlugin.html @@ -0,0 +1,999 @@ + + + + + + + + RenamerPlugin + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Class

      +

      RenamerPlugin

      +
      + + + + + +
      + +
      + +

      RenamerPlugin(options)

      + +
      Bruh
      + + +
      + +
      +
      + + +
      +
      +
      +
      + Constructor +
      + + + + +

      + # + + + + new RenamerPlugin(options) + + +

      + + + + +
      + Intializes the plugin in the webpack build process +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      options + + +offliineSchema + + + +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + renamer/RenamerPlugin.js, line 32 + +

      + +
      + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      + + +
      + + + + + + + + + + + + + + + + +
      +

      Methods

      +
      + +
      + + + +

      + # + + + + apply(compiler) + + +

      + + + + +
      + Hook into the compilation process, Replace regex matches with random strings +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      compiler + + +Webpack.compiler + + + + object from webpack
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + renamer/RenamerPlugin.js, line 96 + +

      + +
      + + + + + + + + + + + + + + + + + + + + + +
      + +
      + + + +

      + # + + + + getRandomName(src) → {string} + + +

      + + + + +
      + Get a random variable name, which does not exist in src and mappings +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      src + + +string + + + +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + renamer/RenamerPlugin.js, line 54 + +

      + +
      + + + + + + + + + + + + + + + + + + +
      +
      +
      + + + +
      + + +
      + + +string + + +
      + +
      + + +
      +
      + + + + +
      + +
      + + + +

      + # + + + + processString(source) → {string} + + +

      + + + + +
      + replace all regex matches with random variable names +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      source + + +string + + + +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + renamer/RenamerPlugin.js, line 87 + +

      + +
      + + + + + + + + + + + + + + + + + + +
      +
      +
      + + + +
      + + +
      + + +string + + +
      + +
      + + +
      +
      + + + + +
      + +
      + + + +

      + # + + + + replaceMatch(source, match) → {string} + + +

      + + + + +
      + Regex replace "match" function +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      source + + +string + + + +
      match + + +string + + + +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + renamer/RenamerPlugin.js, line 69 + +

      + +
      + + + + + + + + + + + + + + + + + + +
      +
      +
      + + + +
      + + +
      + + +string + + +
      + +
      + + +
      +
      + + + + +
      + +
      +
      + + + + + +
      + +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + \ No newline at end of file diff --git a/docs/RenderPass.html b/docs/RenderPass.html index f98b54d..ca4243b 100644 --- a/docs/RenderPass.html +++ b/docs/RenderPass.html @@ -66,7 +66,7 @@ @@ -451,7 +451,155 @@

      View Source - three/pass/RenderPass.ts, line 35 + three/pass/RenderPass.ts, line 42 + +

      + + + + + + + + + + + + + + + + + + + + + + + + + +
      + + + +

      + # + + + + prepare(renderer) + + +

      + + + + +
      + precompile shader +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      renderer + + +WebGLRenderer + + + +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + three/pass/RenderPass.ts, line 36

      @@ -724,7 +872,7 @@
      Parameters:

      View Source - three/pass/RenderPass.ts, line 54 + three/pass/RenderPass.ts, line 61

      @@ -897,7 +1045,7 @@
      Parameters:

      View Source - three/pass/RenderPass.ts, line 43 + three/pass/RenderPass.ts, line 50

      diff --git a/docs/ShaderPass.html b/docs/ShaderPass.html index fdf7b1d..8353bb9 100644 --- a/docs/ShaderPass.html +++ b/docs/ShaderPass.html @@ -66,7 +66,7 @@
      @@ -377,6 +377,154 @@

      + + + + + + + + + + + +

      + View Source + + three/pass/ShaderPass.ts, line 54 + +

      + + + + + + + + + + + + + + + + + + + + + + + + + +
      + + + +

      + # + + + + prepare(renderer) + + +

      + + + + +
      + precompile shader +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      renderer + + +WebGLRenderer + + + +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + @@ -639,7 +787,7 @@
      Parameters:

      View Source - three/pass/ShaderPass.ts, line 68 + three/pass/ShaderPass.ts, line 75

      @@ -812,7 +960,7 @@
      Parameters:

      View Source - three/pass/ShaderPass.ts, line 56 + three/pass/ShaderPass.ts, line 63

      diff --git a/docs/Smallog.ts.html b/docs/Smallog.ts.html index 5937c3a..1d5fdaa 100644 --- a/docs/Smallog.ts.html +++ b/docs/Smallog.ts.html @@ -68,7 +68,7 @@
      @@ -98,29 +98,7 @@

      Smallog.ts

      /* eslint-disable no-unused-vars */ /** -* trace exception calls -* @param {string} def error message -* @param {number} depth which call to pick -* @return {string} -* @ignore -*/ -function traceCall(def: string, depth: number = 3) { - try { - throw new Error('TraceCall()'); - } catch (e) { - // Examine e.stack here - if (e.stack) { - const splt = e.stack.split(/\n/); - let trim = splt[depth].trim(); - if (trim.indexOf('at ') == 0) trim = trim.substring(3); - if (splt.length > depth) return '[' + trim + '] '; - } - } - return def; -} - -/** -* @see {Smallog} +* @see {Smalog} * @public */ export enum LogLevel { @@ -142,33 +120,55 @@

      Smallog.ts

      * Small logging util, with name/time prefix & log levels * @public */ -export module Smallog { - const logLevel: LogLevel = 2; - let preFix: string = '[Smallog] '; - let printTime: boolean = false; +class Smalog { + logLevel: LogLevel = LogLevel.Info; + preFix: string = '[Smallog] '; + printTime: boolean = false; + + /** + * trace exception calls + * @param {string} def error message + * @param {number} depth which call to pick + * @return {string} + * @ignore + */ + traceCall(def: string, depth: number = 3) { + try { + throw new Error('TraceCall()'); + } catch (e) { + // Examine e.stack here + if (e.stack) { + const splt = e.stack.split(/\n/); + let trim = splt[depth].trim(); + if (trim.indexOf('at ') == 0) trim = trim.substring(3); + if (splt.length > depth) return '[' + trim + '] '; + } + } + return def; + } /** * get logging output level * @return {LogLevel} current */ - export function getLevel() { - return logLevel; + getLevel() { + return this.logLevel; } /** * set logging prefix * @param {string} pre */ - export function setPrefix(pre: string) { - preFix = pre; + setPrefix(pre: string) { + this.preFix = pre; } /** * set time prefix * @param {boolean} print */ - export function setPrintTime(print: boolean) { - printTime = print; + setPrintTime(print: boolean) { + this.printTime = print; } /** @@ -176,8 +176,8 @@

      Smallog.ts

      * @param {string} msg log * @param {string} hdr overwrite header */ - export function error(msg: string, hdr: string = preFix) { - if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + error(msg: string, hdr: string = this.preFix) { + if (this.printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; console.error(hdr + msg); } @@ -186,10 +186,10 @@

      Smallog.ts

      * @param {string} msg log * @param {string} hdr overwrite header */ - export function info(msg: string, hdr: string = preFix) { - if (logLevel >= 1) { - if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; - if (logLevel >= 2) hdr = traceCall(hdr); + info(msg: string, hdr: string = this.preFix) { + if (this.logLevel >= 1) { + if (this.printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + if (this.logLevel >= 2) hdr = this.traceCall(hdr); console.info(hdr + msg); } } @@ -199,13 +199,15 @@

      Smallog.ts

      * @param {string} msg log * @param {string} hdr overwrite header */ - export function debug(msg: string, hdr: string = preFix) { - if (logLevel >= 2) { - if (printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + debug(msg: string, hdr: string = this.preFix) { + if (this.logLevel >= 2) { + if (this.printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; console.debug(hdr + msg); } } } + +export const Smallog = new Smalog(); diff --git a/docs/Smalog.html b/docs/Smalog.html new file mode 100644 index 0000000..5fcdd36 --- /dev/null +++ b/docs/Smalog.html @@ -0,0 +1,1195 @@ + + + + + + + + Smalog + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Class

      +

      Smalog

      +
      + + + + + +
      + +
      + +

      Smalog()

      + +
      Small logging util, with name/time prefix & log levels
      + + +
      + +
      +
      + + +
      +
      +
      +
      + Constructor +
      + + + + +

      + # + + + + new Smalog() + + +

      + + + + + + + + + + + + + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + Smallog.ts, line 34 + +

      + +
      + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      + + +
      + + + + + + + + + + + + + + + + +
      +

      Methods

      +
      + +
      + + + +

      + # + + + + debug(msg, hdr) + + +

      + + + + +
      + print debug message +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      msg + + +string + + + + log
      hdr + + +string + + + + overwrite header
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + Smallog.ts, line 114 + +

      + +
      + + + + + + + + + + + + + + + + + + + + + +
      + +
      + + + +

      + # + + + + error(msg, hdr) + + +

      + + + + +
      + print error message +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      msg + + +string + + + + log
      hdr + + +string + + + + overwrite header
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + Smallog.ts, line 90 + +

      + +
      + + + + + + + + + + + + + + + + + + + + + +
      + +
      + + + +

      + # + + + + getLevel() → {LogLevel} + + +

      + + + + +
      + get logging output level +
      + + + + + + + + + + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + Smallog.ts, line 68 + +

      + +
      + + + + + + + + + + + + + + + + + + +
      +
      +
      + + + +
      + +
      current
      + + +
      + + +LogLevel + + +
      + +
      + + +
      +
      + + + + +
      + +
      + + + +

      + # + + + + info(msg, hdr) + + +

      + + + + +
      + print info message +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      msg + + +string + + + + log
      hdr + + +string + + + + overwrite header
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + Smallog.ts, line 100 + +

      + +
      + + + + + + + + + + + + + + + + + + + + + +
      + +
      + + + +

      + # + + + + setPrefix(pre) + + +

      + + + + +
      + set logging prefix +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      pre + + +string + + + +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + Smallog.ts, line 75 + +

      + +
      + + + + + + + + + + + + + + + + + + + + + +
      + +
      + + + +

      + # + + + + setPrintTime(print) + + +

      + + + + +
      + set time prefix +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      print + + +boolean + + + +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + Smallog.ts, line 82 + +

      + +
      + + + + + + + + + + + + + + + + + + + + + +
      + +
      +
      + + + + + +
      + +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + \ No newline at end of file diff --git a/docs/UnrealBloomPass.html b/docs/UnrealBloomPass.html index eef134f..36a74ac 100644 --- a/docs/UnrealBloomPass.html +++ b/docs/UnrealBloomPass.html @@ -66,7 +66,7 @@ @@ -426,7 +426,7 @@

      View Source - three/pass/UnrealBloomPass.ts, line 126 + three/pass/UnrealBloomPass.ts, line 133

      @@ -574,7 +574,7 @@

      Parameters:

      View Source - three/pass/UnrealBloomPass.ts, line 277 + three/pass/UnrealBloomPass.ts, line 284

      @@ -745,7 +745,7 @@
      Parameters:

      View Source - three/pass/UnrealBloomPass.ts, line 225 + three/pass/UnrealBloomPass.ts, line 232

      @@ -793,6 +793,154 @@
      Parameters:
      + + + +
      + + + +

      + # + + + + prepare(renderer) + + +

      + + + + +
      + precompile shader +
      + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      renderer + + +WebGLRenderer + + + +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + three/pass/UnrealBloomPass.ts, line 127 + +

      + +
      + + + + + + + + + + + + + + + + + + + +
      @@ -1016,7 +1164,7 @@
      Parameters:

      View Source - three/pass/UnrealBloomPass.ts, line 162 + three/pass/UnrealBloomPass.ts, line 169

      @@ -1189,7 +1337,7 @@
      Parameters:

      View Source - three/pass/UnrealBloomPass.ts, line 141 + three/pass/UnrealBloomPass.ts, line 148

      diff --git a/docs/Util.ts.html b/docs/Util.ts.html index 1084d76..8fd38e4 100644 --- a/docs/Util.ts.html +++ b/docs/Util.ts.html @@ -68,7 +68,7 @@ @@ -121,6 +121,57 @@

      Util.ts

      promQueue.push(resolve); }); } + +/** +* @todo check for rgba errors +* Convert helper +* @param {string} r_g_b format: "r g b" where each is float 0-1 +* @param {number} mlt multiplier (default 255) +* @return {Object} {r,g,b,a} with float 0-mlt +*/ +export function rgbToObj(r_g_b:string, mlt = 255): {r: number, g:number, b:number, a:number} { + // fix support for rgba strings + const brackI = r_g_b.indexOf('('); + if (brackI > -1) { + r_g_b = r_g_b.substring(brackI + 1, r_g_b.indexOf(')')); + } + // do splitting and multiplying + const spl = r_g_b.split(' ') as any[]; + for (let i = 0; i < spl.length; i++) { + spl[i] = isNaN(spl[i]) ? 0 : Math.min(mlt, Math.max(0, spl[i] * mlt)); + } + return { + r: spl[0] || 0, + g: spl[1] || 0, + b: spl[2] || 0, + a: spl[3] || (1 * mlt), + }; +} + +/** +* Convert helper +* @param {string} r_g_b format: "r g b" where each is float 0-1 +* @return {Object} {h,s,l} with float 0-1 +*/ +export function rgbToHSL(r_g_b: string): {h: number, s:number, l:number} { + const cO = rgbToObj(r_g_b, 1); + const ma = Math.max(cO.r, cO.g, cO.b); + const mi = Math.min(cO.r, cO.g, cO.b); + const hsl = {h: 0, s: 0, l: (mi + ma) / 2}; + const t = ma - mi; + switch (hsl.s = hsl.l <= .5 ? t / (ma + mi) : t / (2 - ma - mi), ma) { + case cO.r: + hsl.h = (cO.g - cO.b) / t + (cO.g < cO.b ? 6 : 0); + break; + case cO.g: + hsl.h = (cO.b - cO.r) / t + 2; + break; + case cO.b: + hsl.h = (cO.r - cO.g) / t + 4; + } + hsl.h /= 6; + return hsl; +} diff --git a/docs/WEAS.html b/docs/WEAS.html index 798d080..26124b3 100644 --- a/docs/WEAS.html +++ b/docs/WEAS.html @@ -66,7 +66,7 @@ @@ -432,7 +432,7 @@

      View Source - WEAS.ts, line 396 + WEAS.ts, line 400

      @@ -570,7 +570,7 @@

      View Source - WEAS.ts, line 387 + WEAS.ts, line 391

      @@ -718,7 +718,7 @@

      View Source - WEAS.ts, line 392 + WEAS.ts, line 396

      @@ -1007,7 +1007,7 @@

      View Source - WEAS.ts, line 322 + WEAS.ts, line 326

      @@ -1231,7 +1231,7 @@

      View Source - WEAS.ts, line 332 + WEAS.ts, line 336

      @@ -1333,7 +1333,7 @@

      View Source - WEAS.ts, line 273 + WEAS.ts, line 276

      diff --git a/docs/WEAS.ts.html b/docs/WEAS.ts.html index db83502..df65bd9 100644 --- a/docs/WEAS.ts.html +++ b/docs/WEAS.ts.html @@ -68,7 +68,7 @@ @@ -293,31 +293,35 @@

      WEAS.ts

      run(({module, instance, exports, params}) => { const ex = instance.exports as any; const {data} = params[0]; + const arrData = new Float64Array(data); // set audio data directly in module memory - exports.__getFloat64ArrayView(ex.inputData).set(data); + exports.__getFloat64ArrayView(ex.inputData).set(arrData); // trigger processing processing ex.update(); // get copy of updated Data & Properties const r = { // => result - data: new Float64Array(exports.__getFloat64ArrayView(ex.outputData)), - props: new Float64Array(exports.__getFloat64ArrayView(ex.audioProps)), + data: new Float64Array(exports.__getFloat64ArrayView(ex.outputData)).buffer, + props: new Float64Array(exports.__getFloat64ArrayView(ex.audioProps)).buffer, }; return r; }, // params passed to worker { - data: self.inBuff, + data: self.inBuff.buffer, }) // worker result, back in main context .then((result) => { const {data, props} = result; - const realProps = self.getProps(props); + const arrData = new Float64Array(data); + const arrProps = new Float64Array(props); + + const realProps = self.getProps(arrProps); const teim = performance.now() - start; // apply actual last Data from worker self.lastAudio = { time: start, ellapsed: teim, - data, + data: arrData, ...realProps, } as any; // print info @@ -432,11 +436,12 @@

      WEAS.ts

      run(({module, instance, exports, params}) => { const ex = instance.exports as any; const {data} = params[0]; - exports.__getFloat64ArrayView(ex.audioSettings).set(data); + const arrDat = new Float64Array(data); + exports.__getFloat64ArrayView(ex.audioSettings).set(arrDat); }, // Data passed to worker { - data: sett, + data: sett.buffer, }) // Back to main context .then(() => { diff --git a/docs/WEASettings.html b/docs/WEASettings.html index a397ecc..9bde5b6 100644 --- a/docs/WEASettings.html +++ b/docs/WEASettings.html @@ -66,7 +66,7 @@ @@ -370,7 +370,7 @@

      View Source - WEAS.ts, line 374 + WEAS.ts, line 378

      diff --git a/docs/WEAudio.html b/docs/WEAudio.html index 94a93af..f0479a3 100644 --- a/docs/WEAudio.html +++ b/docs/WEAudio.html @@ -66,7 +66,7 @@ @@ -135,7 +135,7 @@

      WEAudio

      View Source - WEAS.ts, line 375 + WEAS.ts, line 379

      diff --git a/docs/WEICUE.html b/docs/WEICUE.html index 958b94a..fff2115 100644 --- a/docs/WEICUE.html +++ b/docs/WEICUE.html @@ -66,7 +66,7 @@ @@ -873,7 +873,7 @@
      Parameters:

      View Source - WEICUE.ts, line 377 + WEICUE.ts, line 368

      @@ -975,7 +975,7 @@

      View Source - WEICUE.ts, line 283 + WEICUE.ts, line 284

      diff --git a/docs/WEICUE.ts.html b/docs/WEICUE.ts.html index 5e7363c..472b046 100644 --- a/docs/WEICUE.ts.html +++ b/docs/WEICUE.ts.html @@ -68,7 +68,7 @@ @@ -96,9 +96,10 @@

      WEICUE.ts

      import {CComponent} from './CComponent'; import {CSettings} from './CSettings'; -import {waitReady} from './Util'; +import {rgbToObj, waitReady} from './Util'; import {Smallog} from './Smallog'; import {WEAS} from './WEAS'; +import {ICUE} from './ICUE'; const IMG_SRC = './img/icue.png'; @@ -138,7 +139,8 @@

      WEICUE.ts

      * @extends {CComponent} */ export class WEICUE extends CComponent { - private weas: WEAS = null; + private cue: ICUE; + private weas: WEAS; private holder: HTMLDivElement = null; private texter: HTMLDivElement = null; @@ -171,13 +173,15 @@

      WEICUE.ts

      onPluginLoaded: (name: string, version: string) => { const lower = name.toLocaleLowerCase(); if (lower === 'cue' || lower === 'led') { + this.cue = window['cue']; this.isAvailable = true; Smallog.debug(`Plugin loaded: ${name}, v${version}`, ClassName); } }, }; + + // inject helpers waitReady().then(() => { - // inject helpers this.injectCSS(); this.injectHTML(); this.init(); @@ -247,6 +251,7 @@

      WEICUE.ts

      const imgg = document.createElement('img'); imgg.id = 'icuelogo'; imgg.setAttribute('src', IMG_SRC); + imgg.setAttribute('alt', 'ICUE Icon'); // make text holder this.texter = document.createElement('div'); this.texter.id = 'icuetext'; @@ -407,7 +412,7 @@

      WEICUE.ts

      * @param {number} count Retries (will stop at 100) * @ignore */ - private initCUE(count) { + private initCUE(count: number) { // wait for plugins if (!this.isAvailable) { if (count < 100) setTimeout(() => this.initCUE(++count), 150); @@ -417,13 +422,13 @@

      WEICUE.ts

      // setup devices this.icueDevices = []; - window['cue'].getDeviceCount((deviceCount) => { + this.cue.getDeviceCount((deviceCount) => { this.icueMessage('LED: Found ' + deviceCount + ' devices.'); for (let xi = 0; xi < deviceCount; xi++) { const xl = xi; - window['cue'].getDeviceInfo(xl, (info) => { + this.cue.getDeviceInfo(xl, (info) => { info.id = xl; - window['cue'].getLedPositionsByDeviceIndex(xl, (leds) => { + this.cue.getLedPositionsByDeviceIndex(xl, (leds) => { info.leds = leds; this.icueDevices[xl] = info; }); @@ -433,7 +438,7 @@

      WEICUE.ts

      } /** - * do the thing... + * do the thing... * @ignore */ private updateFrame() { @@ -445,31 +450,22 @@

      WEICUE.ts

      const encDat = this.getEncodedCanvasImageData(this.helperContext.getImageData(0, 0, canvasX, canvasY)); // update all icueDevices with data for (let xi = 0; xi < this.icueDevices.length; xi++) { - window['cue'].setLedColorsByImageData(xi, encDat, canvasX, canvasY); + this.cue.setLedColorsByImageData(xi, encDat, canvasX, canvasY); } } // color mode if (sett.icue_mode == 2) { - // get lol objects - const col = sett.icue_main_color.split(' ') as unknown[]; - let ledColor = { - r: col[0] as number * 255, - g: col[1] as number * 255, - b: col[2] as number * 255, - }; ; // try audio multiplier processing + let mlt = 255; if (this.weas.hasAudio()) { const aud = this.weas.lastAudio; - const mlt = 255 * aud.average / aud.range / aud.intensity * 10; - ledColor = { - r: Math.min(255, Math.max(0, col[0] as number * mlt)), - g: Math.min(255, Math.max(0, col[1] as number * mlt)), - b: Math.min(255, Math.max(0, col[2] as number * mlt)), - }; + mlt *= aud.average / aud.range / aud.intensity * 10; } + // get lol objects + const ledCol = rgbToObj(sett.icue_main_color, mlt); // update all icueDevices with data for (let xi = 0; xi < this.icueDevices.length; xi++) { - window['cue'].setAllLedsColorsAsync(xi, ledColor); + this.cue.setAllLedsColorsAsync(xi, ledCol as any); } } } @@ -488,10 +484,8 @@

      WEICUE.ts

      const area: any = this.getArea(false); const hctx = this.helperContext; // get real rgb values - const spl = sett.main_color.split(' ') as unknown[]; - for (let i = 0; i < spl.length; i++) spl[i] = (spl[i] as number * 255); - // overlay "decay" style - hctx.fillStyle = 'rgba(' + spl.join(', ') + ', ' + sett.icue_area_decay / 100 + ')'; + const cO = rgbToObj(sett.main_color); + hctx.fillStyle = `rgba(${cO.r}, ${cO.g}, ${cO.b}, ${sett.icue_area_decay / 100})`; hctx.fillRect(0, 0, canvasX, canvasY); // scale down and copy the image to the helper canvas hctx.drawImage(mainCanvas, area.left, area.top, area.width, area.height, 0, 0, canvasX, canvasY); diff --git a/docs/WEWA.ts.html b/docs/WEWA.ts.html index 87df985..dc3bee8 100644 --- a/docs/WEWA.ts.html +++ b/docs/WEWA.ts.html @@ -68,7 +68,7 @@ @@ -96,9 +96,8 @@

      WEWA.ts

      import {waitReady} from './Util'; import {Smallog} from './Smallog'; -import {register, reset} from './offline/OfflineHelper'; -import {CC} from 'cookieconsent'; -import {myFetch} from './wasc-worker/WascUtil'; +import {OfflineHelper} from './offline/OfflineHelper'; +import {WascUtil} from './wasc-worker/WascUtil'; const LogHead = '[WEWWA] '; const DefLang = 'de-de'; @@ -145,8 +144,6 @@

      WEWA.ts

      * - react to changes made in the ui and update them in the wallpaper * <br/> * - save changes made in the ui to localStorage -* <br/> -* - Annoying Cookie Popup (Thanks DSGVO) * * * @todo @@ -200,20 +197,8 @@

      WEWA.ts

      // intialize when ready waitReady().then(() => { - if (CC) {/* This tells the compiler to include CookieConsent at this point. */} - - // Thanks DSGVO... - window['cookieconsent'].initialise({ - palette: { - popup: {background: '#000'}, - button: {background: '#f1d600'}, - }, - position: 'bottom-left', - theme: 'edgeless', - }); - // make the website available offline using service worker - register(document.title.replace(' ', '')).then(() => { + OfflineHelper.register(document.title.replace(' ', '')).then(() => { // continue initializing finished(); this.init(); @@ -230,7 +215,7 @@

      WEWA.ts

      * @ignore */ private init() { - myFetch('project.json', 'json').then((proj) => { + WascUtil.myFetch('project.json', 'json').then((proj) => { if (proj.type != 'web') { Smallog.error('Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...', LogHead); return; @@ -576,7 +561,7 @@

      WEWA.ts

      if (!window.confirm('This action will clear ALL local data!\r\n\r\nAre you sure?')) { return; } - reset().then(() => { + OfflineHelper.reset().then(() => { localStorage.clear(); location = location; }); @@ -968,7 +953,7 @@

      WEWA.ts

      * @ignore */ private loadXHRSaveLocal(url, resCall) { - myFetch(url, 'blob').then((resp) => { + WascUtil.myFetch(url, 'blob').then((resp) => { // Read out file contents as a Data URL const fReader = new FileReader(); // onload needed since Google Chrome doesn't support addEventListener for FileReader diff --git a/docs/WEWWA.html b/docs/WEWWA.html index 3a7ab16..ba630ba 100644 --- a/docs/WEWWA.html +++ b/docs/WEWWA.html @@ -66,7 +66,7 @@ @@ -87,7 +87,7 @@

      WEWWA

      WEWWA(finished)

      -
      WEWWA
      Wallpaper Engine Web Wallpaper Adapter
      This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine Web-Wallpaper project - so you can test, run & configure it from a normal web browser.
      REQUIREMENTS:
      - HTML5 Browser
      - the "project.json" needs to be in the root folder like "index.html"
      - this file needs to be included/built in your "index.html"

      FEATURES:
      - automatically detecting if the web wallpaper is opened by wallpaper engine or browser
      - if opened by wallpaper engine, nothing will happen
      - if opened by a browser:
      - use a ServiceWorker to make page always available offline
      - automatically load the "project.json"
      - parse the settings, languages & conditions
      - add respective html elements for each setting type & condition
      - put these elements into an option menu which can be hidden
      - check localStorage for already saved/customized values
      - apply all settings once
      - react to changes made in the ui and update them in the wallpaper
      - save changes made in the ui to localStorage
      - Annoying Cookie Popup (Thanks DSGVO)
      +
      WEWWA
      Wallpaper Engine Web Wallpaper Adapter
      This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine Web-Wallpaper project - so you can test, run & configure it from a normal web browser.
      REQUIREMENTS:
      - HTML5 Browser
      - the "project.json" needs to be in the root folder like "index.html"
      - this file needs to be included/built in your "index.html"

      FEATURES:
      - automatically detecting if the web wallpaper is opened by wallpaper engine or browser
      - if opened by wallpaper engine, nothing will happen
      - if opened by a browser:
      - use a ServiceWorker to make page always available offline
      - automatically load the "project.json"
      - parse the settings, languages & conditions
      - add respective html elements for each setting type & condition
      - put these elements into an option menu which can be hidden
      - check localStorage for already saved/customized values
      - apply all settings once
      - react to changes made in the ui and update them in the wallpaper
      - save changes made in the ui to localStorage
      @@ -229,7 +229,7 @@
      Parameters:

      View Source - WEWA.ts, line 74 + WEWA.ts, line 71

      @@ -358,7 +358,7 @@

      View Source - WEWA.ts, line 916 + WEWA.ts, line 903

      @@ -455,7 +455,7 @@

      View Source - WEWA.ts, line 847 + WEWA.ts, line 834

      @@ -552,7 +552,7 @@

      View Source - WEWA.ts, line 927 + WEWA.ts, line 914

      @@ -649,7 +649,7 @@

      View Source - WEWA.ts, line 775 + WEWA.ts, line 762

      @@ -746,7 +746,7 @@

      View Source - WEWA.ts, line 1053 + WEWA.ts, line 1040

      diff --git a/docs/WarnHelper.html b/docs/WarnHelper.html index db2d281..0edd50b 100644 --- a/docs/WarnHelper.html +++ b/docs/WarnHelper.html @@ -66,7 +66,7 @@ @@ -669,7 +669,7 @@

      View Source - WarnHelper.ts, line 106 + WarnHelper.ts, line 107

      @@ -791,7 +791,7 @@

      View Source - WarnHelper.ts, line 84 + WarnHelper.ts, line 85

      @@ -1020,7 +1020,7 @@

      View Source - WarnHelper.ts, line 120 + WarnHelper.ts, line 121

      diff --git a/docs/WarnHelper.ts.html b/docs/WarnHelper.ts.html index 25effc4..07d13e5 100644 --- a/docs/WarnHelper.ts.html +++ b/docs/WarnHelper.ts.html @@ -68,7 +68,7 @@ @@ -167,6 +167,7 @@

      WarnHelper.ts

      this.element = document.createElement('img'); this.element.id = ELM_ID; this.element.setAttribute('src', IMG_SRC); + this.element.setAttribute('alt', 'Seizure Warning'); document.body.append(this.element); } @@ -216,7 +217,7 @@

      WarnHelper.ts

      public updateSettings(): Promise<void> { // fix for instantly removing the warning while it shows if (!this.settings.seizure_warning && this.element.classList.contains('show')) { - return this.hide(); + this.hide(); } // whatever return Promise.resolve(); diff --git a/docs/WarnSettings.html b/docs/WarnSettings.html index fc10042..f90db0f 100644 --- a/docs/WarnSettings.html +++ b/docs/WarnSettings.html @@ -66,7 +66,7 @@ diff --git a/docs/XRHelper.html b/docs/XRHelper.html index cf42347..bd4c901 100644 --- a/docs/XRHelper.html +++ b/docs/XRHelper.html @@ -66,7 +66,7 @@ @@ -171,7 +171,7 @@

      View Source - three/XRHelper.ts, line 20 + three/XRHelper.ts, line 21

      @@ -669,7 +669,7 @@

      View Source - three/XRHelper.ts, line 33 + three/XRHelper.ts, line 36

      @@ -821,7 +821,7 @@

      Parameters:

      View Source - three/XRHelper.ts, line 72 + three/XRHelper.ts, line 75

      @@ -941,7 +941,7 @@

      View Source - three/XRHelper.ts, line 60 + three/XRHelper.ts, line 63

      diff --git a/docs/XRSettings.html b/docs/XRSettings.html index 6413c22..30c72c9 100644 --- a/docs/XRSettings.html +++ b/docs/XRSettings.html @@ -66,7 +66,7 @@ @@ -167,7 +167,7 @@

      View Source - three/XRHelper.ts, line 8 + three/XRHelper.ts, line 9

      diff --git a/docs/global.html b/docs/global.html index 5966f8b..532f60e 100644 --- a/docs/global.html +++ b/docs/global.html @@ -66,7 +66,7 @@ @@ -203,7 +203,7 @@

      See:
      @@ -214,7 +214,7 @@

      View Source - Smallog.ts, line 39 + Smallog.ts, line 15

      @@ -283,7 +283,7 @@

      View Source - Smallog.ts, line 44 + Smallog.ts, line 20

      @@ -352,7 +352,7 @@

      View Source - Smallog.ts, line 48 + Smallog.ts, line 24

      @@ -421,7 +421,7 @@

      View Source - Smallog.ts, line 52 + Smallog.ts, line 28

      @@ -515,15 +515,15 @@

      -

      - # +

      + # constant - PropIDs + offlineSchema

      @@ -531,6 +531,10 @@

      +
      + schema for options object +
      + @@ -562,15 +566,22 @@

      + +
      See:
      +
      + +

      - View Source + View Source - WEAS.ts, line 71 + renamer/RenamerPlugin.js, line 20

      @@ -584,15 +595,15 @@

      -

      - # +

      + # constant - SettIDs + PropIDs

      @@ -639,7 +650,7 @@

      View Source - WEAS.ts, line 55 + WEAS.ts, line 71

      @@ -653,11 +664,15 @@

      -

      - # +

      + # + + + constant + - Smallog + SettIDs

      @@ -665,10 +680,6 @@

      -
      - Small logging util, with name/time prefix & log levels -
      - @@ -706,9 +717,9 @@

      - View Source + View Source - Smallog.ts, line 58 + WEAS.ts, line 55

      @@ -997,6 +1008,8 @@

      Parameters:
      + Default + Description @@ -1023,6 +1036,10 @@
      Parameters:
      + + + + @@ -1048,6 +1065,12 @@
      Parameters:
      + + + Offline.worker.js + + + @@ -1073,6 +1096,12 @@
      Parameters:
      + + + offlinefiles.json + + + @@ -1180,6 +1209,10 @@
      Parameters:

      # + + async + + reset() → {Promise.<boolean>} @@ -1299,12 +1332,12 @@

      -

      - # +

      + # - waitReady() → {Promise} + rgbToHSL(r_g_b) → {Object}

      @@ -1313,7 +1346,7 @@

      - Shorthand Document ready wrapper,\n resolves promise when html document is ready + Convert helper
      @@ -1325,6 +1358,57 @@

      +

      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      r_g_b + + +string + + + + format: "r g b" where each is float 0-1
      +
      + @@ -1364,7 +1448,7 @@

      View Source - Util.ts, line 26 + Util.ts, line 66

      @@ -1395,11 +1479,13 @@

      +
      {h,s,l} with float 0-1
      +
      -Promise +Object
      @@ -1415,11 +1501,1366 @@

      -

      -

      +
      + + + +

      + # + + + + rgbToObj(r_g_b, mlt) → {Object} + + +

      + + + + + + + + + + + + + +
      Parameters:
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      r_g_b + + +string + + + + format: "r g b" where each is float 0-1
      mlt + + +number + + + + multiplier (default 255)
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      To Do:
      +
      +
        +
      • check for rgba errors Convert helper
      • +
      +
      +

      + View Source + + Util.ts, line 43 + +

      + +
      + + + + + + + + + + + + + + + + + + +
      +
      +
      + + + +
      + +
      {r,g,b,a} with float 0-mlt
      + + +
      + + +Object + + +
      + +
      + + +
      +
      + + + + +
      + +
      + + + +

      + # + + + + waitReady() → {Promise} + + +

      + + + + +
      + Shorthand Document ready wrapper,\n resolves promise when html document is ready +
      + + + + + + + + + + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + Util.ts, line 26 + +

      + +
      + + + + + + + + + + + + + + + + + + +
      +
      +
      + + + +
      + + +
      + + +Promise + + +
      + +
      + + +
      +
      + + + + +
      + +

      + + + + +
      +

      Type Definitions

      +
      + +
      + + + + +object + + + + +

      + # + + + DeviceInfo + + +

      + + + + +
      + Contains information about device. +
      + + + + + +
      Properties:
      + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      id + + +number + + + + ICUE Device ID (not set by default)
      type + + +DeviceType + + + + Enum describing device type.
      model + + +string + + + + Device model (like "K95RGB").
      physicalLayout + + +PhysicalLayout + + + + Enum describing physical layout of the keyboard or mouse. , +If device is neither keyboard nor mouse then value is "CPL_Invalid".
      logicalLayout + + +LogicalLayout + + + + Enum describing logical layout of the keyboard as set in CUE settings. , +If device is not keyboard then value is "CLL_Invalid".
      ledCount + + +number + + + + Number of controllable LEDs on the device.
      leds + + +Array + + + + Led positions on the device (not set by default)
      capsMask + + +object +| + +object + + + + Contains list of device capabilities. , +First version of SDK only supports lighting, but future versions may also support other capabilities.
      CDC_Lighting + + +boolean + + + +
      CDC_None + + +boolean + + + +
      +
      + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + ICUE.d.ts, line 29 + +

      + +
      + + + + + +
      + +
      + + + + +object + + + + +

      + # + + + LedColor + + +

      + + + + +
      + Contains information about led and its color. +
      + + + + + +
      Properties:
      + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      ledId + + +number + + + + Identifier of LED to set.
      r + + +number + + + + Red brightness [0..255].
      g + + +number + + + + Green brightness [0..255].
      b + + +number + + + + Blue brightness [0..255].
      +
      + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + ICUE.d.ts, line 68 + +

      + +
      + + + + + +
      + +
      + + + + +object + + + + +

      + # + + + LedPosition + + +

      + + + + +
      + Contains led id and position of led rectangle. Most of the keys are rectangular. In case if key is not rectangular (like Enter in ISO/UK layout) it returns the smallest rectangle that fully contains the key. +
      + + + + + +
      Properties:
      + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      height + + +number + + + + For keyboards, mousemats and headset stands, height in mm; , +for DIY-devices, height in logical units.
      width + + +number + + + + For keyboards, mousemats and headset stands, width in mm; , +for DIY-devices, width in logical units.
      top + + +number + + + + For keyboards, mousemats and headset stands, top offset in mm; , +for DIY-devices, top offset in logical units.
      left + + +number + + + + For keyboards, mousemats and headset stands, left offset in mm; , +for DIY-devices, left offset in logical units.
      ledId + + +number + + + + Identifier of led.
      ledIdName + + +string + + + + Identifier of led.
      +
      + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      + View Source + + ICUE.d.ts, line 49 + +

      + +
      + + + + + +
      + +
      + + + + +object + + + + +

      + # + + + ProtocolDetails + + +

      + + + + + + + + +
      Properties:
      + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      breakingChanges + + +boolean + + + + Boolean value that specifies if there were breaking changes between version of protocol implemented by server and client.
      sdkProtocolVersion + + +number + + + + Integer number that specifies version of protocol that is implemented by current SDK. , +Numbering starts from 1. Always contains valid value even if there was no CUE found.
      sdkVersion + + +string + + + + String containing version of SDK (like "1.0.0.1"). Always contains valid value even if there was no CUE found.
      serverProtocolVersion + + +number + + + + Integer number that specifies version of protocol that is implemented by CUE. , +Numbering starts from 1. If CUE was not found then this value will be 0.
      serverVersion + + +number + + + + String containing version of CUE (like "1.0.0.1") or NULL if CUE was not found.
      +
      + + + +
      + + + + + + + + + + + + + + + + + + +
      Author:
      +
      +
        +
      • 'Andrew' / https://github.com/profezzional Types for Wallpaper Engine integation for CUE SDK 3.0.+
      • +
      +
      + + + + + + + + + + +
      See:
      +
      + +
      + + + + + +

      + View Source + + ICUE.d.ts, line 4 + +

      + +
      + + + + + +
      + +
      +
      + diff --git a/docs/index.html b/docs/index.html index 42de973..c653339 100644 --- a/docs/index.html +++ b/docs/index.html @@ -66,7 +66,7 @@ diff --git a/docs/offline_OfflineHelper.ts.html b/docs/offline_OfflineHelper.ts.html index 4be93d8..1c92f77 100644 --- a/docs/offline_OfflineHelper.ts.html +++ b/docs/offline_OfflineHelper.ts.html @@ -68,7 +68,7 @@ @@ -136,7 +136,7 @@

      offline/OfflineHelper.ts

      * @param {string} oFile * @return {Promise<boolean>} */ -export function register(name: string, worker = 'Offline.worker.js', oFile = 'offlinefiles.json') { +function register(name: string, worker: string = 'Offline.worker.js', oFile: string = 'offlinefiles.json'): Promise<boolean> { return new Promise(async (resolve) => { if ('serviceWorker' in navigator) { const workerPath = `${worker}?name=${name}&jsonPath=${oFile}`; @@ -157,7 +157,7 @@

      offline/OfflineHelper.ts

      * @return {Promise<boolean>} finished * @public */ -export async function reset() { +async function reset(): Promise<boolean> { return new Promise((resolve) => { if ('serviceWorker' in navigator) { navigator.serviceWorker.getRegistrations().then(async (registrations) => { @@ -172,6 +172,8 @@

      offline/OfflineHelper.ts

      } }); } + +export const OfflineHelper = {register, reset}; diff --git a/docs/offline_OfflinePlugin.js.html b/docs/offline_OfflinePlugin.js.html index c7a8c80..d424620 100644 --- a/docs/offline_OfflinePlugin.js.html +++ b/docs/offline_OfflinePlugin.js.html @@ -68,7 +68,7 @@ diff --git a/docs/renamer_RenamerPlugin.js.html b/docs/renamer_RenamerPlugin.js.html new file mode 100644 index 0000000..17f1873 --- /dev/null +++ b/docs/renamer_RenamerPlugin.js.html @@ -0,0 +1,247 @@ + + + + + + + + + + renamer/RenamerPlugin.js + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + +
      +
      +
      + +
      +
      +
      +

      Source

      +

      renamer/RenamerPlugin.js

      +
      + + + + + +
      +
      +
      /**
      +* @author hexxone / https://hexx.one
      +*
      +* @license
      +* Copyright (c) 2021 hexxone All rights reserved.
      +* Licensed under the GNU GENERAL PUBLIC LICENSE.
      +* See LICENSE file in the project root for full license information.
      +* @ignore
      +*/
      +
      +const {RawSource} = require('webpack-sources');
      +
      +const validate = require('schema-utils');
      +const pluginName = 'RenamerPlugin';
      +
      +/**
      +* schema for options object
      +* @see {RenamerPlugin}
      +*/
      +const offlineSchema = {
      +	type: 'object',
      +	properties: {
      +		regex: {
      +			type: 'object',
      +		},
      +	},
      +};
      +
      +/**
      +* Bruh
      +*/
      +class RenamerPlugin {
      +	options = {};
      +
      +	// mapped name list: {key,value}
      +	nameMap = [];
      +	// saved character count
      +	savedChars = 0;
      +
      +	/**
      +	* Intializes the plugin in the webpack build process
      +	* @param {offliineSchema} options
      +	*/
      +	constructor(options = {}) {
      +		validate.validate(offlineSchema, options);
      +		this.options = options;
      +	}
      +
      +	/**
      +	* Get a random variable name, which does not exist in src and mappings
      +	* @param {string} src
      +	* @return {string}
      +	*/
      +	getRandomName(src) {
      +		const gen = 'hx' + Math.random().toString(36).substr(2, 3) + Math.floor(10 + Math.random() * 89);
      +		let exist = (src || '').indexOf(gen) >= 0;
      +		this.nameMap.forEach((mping) => {
      +			if (mping.key === gen) exist = true;
      +		});
      +		return exist ? this.getRandomName() : gen;
      +	}
      +
      +	/**
      +	* Regex replace "match" function
      +	* @param {string} source
      +	* @param {string} match
      +	* @return {string}
      +	*/
      +	replaceMatch(source, match) {
      +		let fnd = null;
      +		// check if this exact name is already mapped
      +		this.nameMap.forEach((mping) => {
      +			if (mping.key === match) fnd = mping.val;
      +		});
      +		if (fnd) return fnd;
      +		// get & add a new random variable name
      +		fnd = this.getRandomName(source);
      +		this.nameMap.push({key: match, val: fnd});
      +		return fnd;
      +	}
      +
      +	/**
      +	* replace all regex matches with random variable names
      +	* @param {string} source
      +	* @return {string}
      +	*/
      +	processString(source) {
      +		return source.replace(this.options.regex, (match) => this.replaceMatch(source, match)); // .replaceAll('const ', 'var ');
      +	}
      +
      +	/**
      +	* Hook into the compilation process,
      +	* Replace regex matches with random strings
      +	* @param {Webpack.compiler} compiler object from webpack
      +	*/
      +	apply(compiler) {
      +		compiler.hooks.emit.tap(pluginName, (compilation) => {
      +			try {
      +				console.info('[' + pluginName + '] Using Regex: ' + this.options.regex);
      +
      +				// process all compiled .js files
      +				for (const assetFile in compilation.assets) {
      +					if (!assetFile || !assetFile.endsWith('.js')) continue;
      +					console.info('[' + pluginName + '] Processing: ' + assetFile);
      +
      +					// get the processed asset object / source
      +					const asset = compilation.getAsset(assetFile);
      +					const source = asset.source._value;
      +					const processed = this.processString(source);
      +
      +					// if anything changed, update the processed asset
      +					if (source != processed) {
      +						compilation.updateAsset(assetFile, new RawSource(processed));
      +						// calculate saved memory
      +						this.savedChars += (source.length - processed.length);
      +					}
      +				}
      +
      +				// finish up
      +				console.info('[' + pluginName + '] Replaced: ', this.nameMap);
      +				console.info('[' + pluginName + '] Saved: ' + this.savedChars + ' chars');
      +			} catch (error) {
      +				console.info('[' + pluginName + '] Replace error: ', error);
      +			}
      +		});
      +	}
      +}
      +
      +module.exports = RenamerPlugin;
      +
      +
      +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + diff --git a/docs/three_EffectComposer.ts.html b/docs/three_EffectComposer.ts.html index 3b860b4..9df4981 100644 --- a/docs/three_EffectComposer.ts.html +++ b/docs/three_EffectComposer.ts.html @@ -68,7 +68,7 @@ @@ -108,18 +108,17 @@

      three/EffectComposer.ts

      * @public */ export class EffectComposer { + // given on construct private scene: Scene; private camera: PerspectiveCamera; private renderer: WebGLRenderer; - - private varCam = new PerspectiveCamera(); - - private viewSize: Vector2; - private globalPrecision: string; - private _previousFrameTime = Date.now(); + // runtime + private viewSize: Vector2; + private previousFrame = Date.now(); + // target & origin buffers private defaultTarget: WebGLRenderTarget; private renderWrite: WebGLRenderTarget; @@ -128,11 +127,12 @@

      three/EffectComposer.ts

      private renderRead: WebGLRenderTarget; private readBuffer: WebGLRenderTarget; + // render passes private normPass: RenderPass; private xrPass: RenderPass; + private xrCam = new PerspectiveCamera(); - // render by default - public enabled: boolean = true; + // public public passes: BasePass[] = []; @@ -142,15 +142,16 @@

      three/EffectComposer.ts

      * @param {PerspectiveCamera} camera * @param {WebGLRenderer} renderer * @param {string} globalPrec + * @param {Color} clearCol * @param {WebGLRenderTarget} renderTarget */ - constructor(scene: Scene, camera: PerspectiveCamera, renderer: WebGLRenderer, globalPrec: string = 'mediump', renderTarget?: WebGLRenderTarget) { + constructor(scene: Scene, camera: PerspectiveCamera, renderer: WebGLRenderer, globalPrec: string = 'mediump', clearCol?: any, renderTarget?: WebGLRenderTarget) { this.scene = scene; this.camera = camera; this.renderer = renderer; this.viewSize = renderer.getSize(new Vector2()); - this.varCam = new PerspectiveCamera(camera.fov, camera.aspect, camera.near, camera.far); + this.xrCam = new PerspectiveCamera(camera.fov, camera.aspect, camera.near, camera.far); // use a new default render target if none is given this.defaultTarget = new WebGLRenderTarget(this.viewSize.width, this.viewSize.height, defaultParams); @@ -158,7 +159,7 @@

      three/EffectComposer.ts

      if (renderTarget === undefined) { renderTarget = this.defaultTarget.clone(); - renderTarget.texture.name = 'EffectComposer.wt'; + renderTarget.texture.name = 'EffectComposer.rt'; } // set write buffer for shader pass rendering @@ -167,15 +168,23 @@

      three/EffectComposer.ts

      // set input buffer for shader pass rendering this.renderRead = renderTarget.clone(); - this.renderRead.texture.name = 'EffectComposer.rt'; + this.renderRead.texture.name = 'EffectComposer.rr'; this.readBuffer = this.renderRead; this.passes = []; - this._previousFrameTime = Date.now(); + this.previousFrame = Date.now(); this.globalPrecision = globalPrec; - this.normPass = new RenderPass(scene, camera, null, 0x000000, 1); - this.xrPass = new RenderPass(scene, this.varCam, null, 0x000000, 1); + this.normPass = new RenderPass(scene, camera, null, clearCol, 1); + this.xrPass = new RenderPass(scene, this.xrCam, null, clearCol, 1); + } + + /** + * Precompile all shaders... + */ + public precompile(): void { + this.renderer.compile(this.scene, this.camera); + this.passes.forEach((pass) => pass.prepare(this.renderer)); } /** @@ -223,9 +232,9 @@

      three/EffectComposer.ts

      // deltaTime value is in seconds const dn = performance.now(); if (deltaTime === undefined) { - deltaTime = (dn - this._previousFrameTime) * 0.001; + deltaTime = (dn - this.previousFrame) * 0.001; } - this._previousFrameTime = dn; + this.previousFrame = dn; const size = new Vector2(); this.renderer.getSize( size ); const currentRenderTarget = this.renderer.getRenderTarget(); @@ -258,17 +267,19 @@

      three/EffectComposer.ts

      // position const varPos = view.transform.position; - this.varCam.position.set(camPos.x + varPos.x, + this.xrCam.position.set(camPos.x + varPos.x, camPos.y + (varPos.y - 1.6), camPos.z + varPos.z); // orientation const vo = view.transform.orientation; - this.varCam.setRotationFromQuaternion(new Quaternion(vo.x, vo.y, vo.z, vo.w)); + this.xrCam.setRotationFromQuaternion(new Quaternion(vo.x, vo.y, vo.z, vo.w)); // matrix - this.varCam.projectionMatrix.fromArray(view.projectionMatrix); - this.varCam.near = this.camera.near; + this.xrCam.projectionMatrix.fromArray(view.projectionMatrix); + this.xrCam.near = this.camera.near; + this.xrCam.far = this.camera.far; + this.xrCam.updateProjectionMatrix(); // render const offX = viewSize * i; diff --git a/docs/three_XRHelper.ts.html b/docs/three_XRHelper.ts.html index 343de45..110dbc5 100644 --- a/docs/three_XRHelper.ts.html +++ b/docs/three_XRHelper.ts.html @@ -68,7 +68,7 @@ @@ -94,6 +94,7 @@

      three/XRHelper.ts

      import {Navigator, XRSession} from 'three'; import {CComponent} from '../CComponent'; import {CSettings} from '../CSettings'; +import {waitReady} from '../Util'; /** * XR Settings @@ -121,8 +122,10 @@

      three/XRHelper.ts

      */ constructor() { super(); - this.nav = navigator as Navigator; - this.createBtn(); + waitReady().then(() => { + this.nav = navigator as Navigator; + this.createBtn(); + }); } /** diff --git a/docs/three_pass_BasePass.ts.html b/docs/three_pass_BasePass.ts.html index b931016..0718a23 100644 --- a/docs/three_pass_BasePass.ts.html +++ b/docs/three_pass_BasePass.ts.html @@ -68,7 +68,7 @@ @@ -85,7 +85,9 @@

      three/pass/BasePass.ts

      -
      /**
      +            
      import {WebGLRenderer} from 'three';
      +
      +/**
       * @author alteredq / http://alteredqualia.com/
       * @author hexxone / https://hexx.one
       *
      @@ -106,6 +108,8 @@ 

      three/pass/BasePass.ts

      // if set to true, the pass clears its buffer before rendering clear: boolean; // = false; + prepare(renderer: WebGLRenderer); + dispose(); setSize(width: number, height: number); diff --git a/docs/three_pass_FullScreenHelper.ts.html b/docs/three_pass_FullScreenHelper.ts.html index 6b96693..5a29e15 100644 --- a/docs/three_pass_FullScreenHelper.ts.html +++ b/docs/three_pass_FullScreenHelper.ts.html @@ -68,7 +68,7 @@ @@ -116,6 +116,14 @@

      three/pass/FullScreenHelper.ts

      this.mesh = new Mesh(this.geometry, material); } + /** + * precompile shader + * @param {WebGLRenderer} renderer + */ + public prepare(renderer: WebGLRenderer) { + renderer.compile(this.mesh, this.camera); + } + /** * Change mesh material * @param {Material} mat diff --git a/docs/three_pass_RenderPass.ts.html b/docs/three_pass_RenderPass.ts.html index 33d0413..35a42d3 100644 --- a/docs/three_pass_RenderPass.ts.html +++ b/docs/three_pass_RenderPass.ts.html @@ -68,7 +68,7 @@ @@ -91,7 +91,7 @@

      three/pass/RenderPass.ts

      * @author hexxone / https://hexx.one */ -import {Camera, Color, Material, Scene} from 'three'; +import {Camera, Color, Material, Scene, WebGLRenderer} from 'three'; import {BasePass} from './BasePass'; /** @@ -131,6 +131,14 @@

      three/pass/RenderPass.ts

      this.clearAlpha = (clearAlpha !== undefined) ? clearAlpha : 0; } + /** + * precompile shader + * @param {WebGLRenderer} renderer + */ + public prepare(renderer: WebGLRenderer) { + renderer.compile(this.scene, this.camera); + } + /** * Destroy shader */ diff --git a/docs/three_pass_ShaderPass.ts.html b/docs/three_pass_ShaderPass.ts.html index 9ee5803..464dead 100644 --- a/docs/three_pass_ShaderPass.ts.html +++ b/docs/three_pass_ShaderPass.ts.html @@ -68,7 +68,7 @@ @@ -139,6 +139,14 @@

      three/pass/ShaderPass.ts

      this.fsQuad = new FullScreenHelper(this.material); } + /** + * precompile shader + * @param {WebGLRenderer} renderer + */ + public prepare(renderer: WebGLRenderer) { + this.fsQuad.prepare(renderer); + } + /** * Destroy Pass * @public diff --git a/docs/three_pass_UnrealBloomPass.ts.html b/docs/three_pass_UnrealBloomPass.ts.html index 3349b11..7568727 100644 --- a/docs/three_pass_UnrealBloomPass.ts.html +++ b/docs/three_pass_UnrealBloomPass.ts.html @@ -68,7 +68,7 @@ @@ -103,38 +103,40 @@

      three/pass/UnrealBloomPass.ts

      * @public */ export class UnrealBloomPass implements BasePass { - name = 'UnrealBloom'; - strength = null; - radius = null; - resolution = null; - threshold = null; - renderTargetBright = null; - highPassUniforms = null; + public name = 'UnrealBloom'; + public enabled = true; + public needsSwap = false; + public clear = true; + + public oldClearColor = new Color(); + public oldClearAlpha = 1; + public clearColor = new Color(0, 0, 0); + + private resolution = null; + private strength = null; + private radius = null; + private threshold = null; + private renderTargetBright = null; + private highPassUniforms = null; // create color only once here, reuse it later inside the render function - clear = true; - clearColor = new Color(0, 0, 0); - renderTargetsHorizontal: WebGLRenderTarget[] = []; - renderTargetsVertical: WebGLRenderTarget[] = []; - nMips = 5; - - separableBlurMaterials: ShaderMaterial[] = []; - materialHighPassFilter: ShaderMaterial = null; - compositeMaterial: ShaderMaterial = null; - materialCopy: ShaderMaterial = null; - bloomTintColors = null; - copyUniforms = null; - enabled = true; - needsSwap = false; - - oldClearColor = new Color(); - oldClearAlpha = 1; - - basic = new MeshBasicMaterial(); - fsQuad = new FullScreenHelper(null); - - BlurDirectionX = new Vector2(1.0, 0.0); - BlurDirectionY = new Vector2(0.0, 1.0); + + private renderTargetsHorizontal: WebGLRenderTarget[] = []; + private renderTargetsVertical: WebGLRenderTarget[] = []; + private nMips = 5; + + private separableBlurMaterials: ShaderMaterial[] = []; + private materialHighPassFilter: ShaderMaterial = null; + private compositeMaterial: ShaderMaterial = null; + private materialCopy: ShaderMaterial = null; + private bloomTintColors = null; + private copyUniforms = null; + + private basic = new MeshBasicMaterial(); + private fsQuad = new FullScreenHelper(null); + + private blurDirX = new Vector2(1.0, 0.0); + private blurDirY = new Vector2(0.0, 1.0); /** * Construct bloom shader @@ -235,6 +237,14 @@

      three/pass/UnrealBloomPass.ts

      }); } + /** + * precompile shader + * @param {WebGLRenderer} renderer + */ + public prepare(renderer: WebGLRenderer) { + this.fsQuad.prepare(renderer); + } + /** * Destroy shader */ @@ -316,13 +326,13 @@

      three/pass/UnrealBloomPass.ts

      this.fsQuad.setMaterial(this.separableBlurMaterials[i]); this.separableBlurMaterials[i].uniforms['colorTexture'].value = inputRenderTarget.texture; - this.separableBlurMaterials[i].uniforms['direction'].value = this.BlurDirectionX; + this.separableBlurMaterials[i].uniforms['direction'].value = this.blurDirX; renderer.setRenderTarget(this.renderTargetsHorizontal[i]); renderer.clear(); this.fsQuad.render(renderer); this.separableBlurMaterials[i].uniforms['colorTexture'].value = this.renderTargetsHorizontal[i].texture; - this.separableBlurMaterials[i].uniforms['direction'].value = this.BlurDirectionY; + this.separableBlurMaterials[i].uniforms['direction'].value = this.blurDirY; renderer.setRenderTarget(this.renderTargetsVertical[i]); renderer.clear(); this.fsQuad.render(renderer); diff --git a/docs/three_shader_BaseShader.ts.html b/docs/three_shader_BaseShader.ts.html index 3bca83c..10e3aba 100644 --- a/docs/three_shader_BaseShader.ts.html +++ b/docs/three_shader_BaseShader.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/three_shader_BlendShader.ts.html b/docs/three_shader_BlendShader.ts.html index 0b0cde2..48a1b91 100644 --- a/docs/three_shader_BlendShader.ts.html +++ b/docs/three_shader_BlendShader.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/three_shader_BlurShader.ts.html b/docs/three_shader_BlurShader.ts.html index f19aa13..b8e292b 100644 --- a/docs/three_shader_BlurShader.ts.html +++ b/docs/three_shader_BlurShader.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/three_shader_ChromaticShader.ts.html b/docs/three_shader_ChromaticShader.ts.html index 49d5f59..5105f67 100644 --- a/docs/three_shader_ChromaticShader.ts.html +++ b/docs/three_shader_ChromaticShader.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/three_shader_CopyShader.ts.html b/docs/three_shader_CopyShader.ts.html index fdd8181..e54ffdc 100644 --- a/docs/three_shader_CopyShader.ts.html +++ b/docs/three_shader_CopyShader.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/three_shader_FXAAShader.ts.html b/docs/three_shader_FXAAShader.ts.html index a2ad5f2..bf9bbb8 100644 --- a/docs/three_shader_FXAAShader.ts.html +++ b/docs/three_shader_FXAAShader.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/three_shader_FractalMirrorShader.ts.html b/docs/three_shader_FractalMirrorShader.ts.html index 18df147..9ff5575 100644 --- a/docs/three_shader_FractalMirrorShader.ts.html +++ b/docs/three_shader_FractalMirrorShader.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/three_shader_LUTShader.ts.html b/docs/three_shader_LUTShader.ts.html index 6416e55..56b5611 100644 --- a/docs/three_shader_LUTShader.ts.html +++ b/docs/three_shader_LUTShader.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/three_shader_LUTShaderNearest.ts.html b/docs/three_shader_LUTShaderNearest.ts.html index 52ea3d6..e61622c 100644 --- a/docs/three_shader_LUTShaderNearest.ts.html +++ b/docs/three_shader_LUTShaderNearest.ts.html @@ -68,7 +68,7 @@ diff --git a/docs/three_shader_LuminosityHighPassShader.ts.html b/docs/three_shader_LuminosityHighPassShader.ts.html index c9dd502..9a439e9 100644 --- a/docs/three_shader_LuminosityHighPassShader.ts.html +++ b/docs/three_shader_LuminosityHighPassShader.ts.html @@ -68,7 +68,7 @@ @@ -114,7 +114,7 @@

      three/shader/LuminosityHighPassShader.ts

      tDiffuse: {value: null}, luminosityThreshold: {value: 1.0}, smoothWidth: {value: 1.0}, - defaultColor: {value: new Color(0x000000)}, + defaultColor: {value: new Color(0x000000)}, // @TODO might need to set to BG color? defaultOpacity: {value: 0.0}, }; diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index 5f93d2e..12e42c8 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -56,6 +56,7 @@ export class ReloadHelper extends CComponent { height: 10px; width: 0%; background-color: #989a; + transition: all 0s; } #reload-bar.show { opacity: 1; @@ -115,12 +116,13 @@ export class ReloadHelper extends CComponent { public show(visible: boolean) { const e1 = document.getElementById('reload-bar'); const e2 = document.getElementById('reload-text'); + e1.classList.remove('show'); + e2.classList.remove('show'); if (visible) { - e1.classList.add('show'); - e2.classList.add('show'); - } else { - e1.classList.remove('show'); - e2.classList.remove('show'); + setTimeout(() => { + e1.classList.add('show'); + e2.classList.add('show'); + }, 100); } } From b97c14117c7a9ad2c7a0ee7fae54d4f591d8f781 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Mon, 6 Sep 2021 16:42:02 +0200 Subject: [PATCH 34/76] move WEAS --- src/{ => weas}/WEAS.ts | 0 src/{ => weas}/WEAS.wasm.asc | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/{ => weas}/WEAS.ts (100%) rename src/{ => weas}/WEAS.wasm.asc (100%) diff --git a/src/WEAS.ts b/src/weas/WEAS.ts similarity index 100% rename from src/WEAS.ts rename to src/weas/WEAS.ts diff --git a/src/WEAS.wasm.asc b/src/weas/WEAS.wasm.asc similarity index 100% rename from src/WEAS.wasm.asc rename to src/weas/WEAS.wasm.asc From a1af6b259a7e7331d8c416fbdb53e0d4fcc3554a Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Tue, 7 Sep 2021 10:28:34 +0200 Subject: [PATCH 35/76] worker update --- src/wasc-worker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasc-worker b/src/wasc-worker index fd75616..a14ae81 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit fd756169015e1aaf561923e04497ef9cbafa6fbd +Subproject commit a14ae811e08b87772df48bc836358427414e7bd1 From fefe077d34c250e16b2baa1f61cc7c48b142aa99 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Mon, 18 Oct 2021 12:47:14 +0200 Subject: [PATCH 36/76] add custom shaders & WebPack loader --- src/three/shader/fragment/Blend.glsl | 11 + src/three/shader/fragment/Blur.glsl | 34 + src/three/shader/fragment/Chromatic.glsl | 20 + src/three/shader/fragment/Copy.glsl | 10 + src/three/shader/fragment/FXAA.glsl | 811 +++++++++++++++++++ src/three/shader/fragment/FractalMirror.glsl | 44 + src/three/shader/fragment/LUT.glsl | 38 + src/three/shader/fragment/Luminosity.glsl | 16 + src/three/shader/loader/glsl.d.ts | 4 + src/three/shader/loader/index.js | 111 +++ src/three/shader/vertex/Basic.glsl | 6 + 11 files changed, 1105 insertions(+) create mode 100644 src/three/shader/fragment/Blend.glsl create mode 100644 src/three/shader/fragment/Blur.glsl create mode 100644 src/three/shader/fragment/Chromatic.glsl create mode 100644 src/three/shader/fragment/Copy.glsl create mode 100644 src/three/shader/fragment/FXAA.glsl create mode 100644 src/three/shader/fragment/FractalMirror.glsl create mode 100644 src/three/shader/fragment/LUT.glsl create mode 100644 src/three/shader/fragment/Luminosity.glsl create mode 100644 src/three/shader/loader/glsl.d.ts create mode 100644 src/three/shader/loader/index.js create mode 100644 src/three/shader/vertex/Basic.glsl diff --git a/src/three/shader/fragment/Blend.glsl b/src/three/shader/fragment/Blend.glsl new file mode 100644 index 0000000..0888640 --- /dev/null +++ b/src/three/shader/fragment/Blend.glsl @@ -0,0 +1,11 @@ +uniform sampler2D tDiffuse; +uniform sampler2D overlayBuffer; + +varying vec2 vUv; + +void main() { + vec4 texel1 = texture2D(tDiffuse, vUv); + vec4 texel2 = texture2D(overlayBuffer, vUv); + vec4 diff = abs(texel1 - texel2); + gl_FragColor = vec4(diff, 1.0); +} \ No newline at end of file diff --git a/src/three/shader/fragment/Blur.glsl b/src/three/shader/fragment/Blur.glsl new file mode 100644 index 0000000..14aaec7 --- /dev/null +++ b/src/three/shader/fragment/Blur.glsl @@ -0,0 +1,34 @@ +varying vec2 vUv; + +uniform sampler2D tDiffuse; +uniform vec2 iResolution; +uniform float u_sigma; +uniform vec2 u_dir; + +float CalcGauss(float x, float sigma) { + if(sigma <= 0.0) + return 0.0; + return exp(-(x * x) / (2.0 * sigma)) / (2.0 * 3.14157 * sigma); +} + +void main() { + vec2 texC = vUv; + vec4 texCol = texture2D(tDiffuse, texC); + vec4 gaussCol = vec4(texCol.rgb, 1.0); + float alphaV = texCol.a; + vec2 step = u_dir / iResolution; + for(int i = 1; i <= 32; ++i) { + float weight = CalcGauss(float(i) / 32.0, u_sigma * 0.5); + if(weight < 1.0 / 255.0) + break; + texCol = texture2D(tDiffuse, texC + step * float(i)); + gaussCol += vec4(texCol.rgb * weight, weight); + alphaV += texCol.a * weight; + texCol = texture2D(tDiffuse, texC - step * float(i)); + gaussCol += vec4(texCol.rgb * weight, weight); + alphaV += texCol.a * weight; + } + alphaV = clamp(alphaV / gaussCol.w, 0.0, 1.0); + gaussCol.rgb = clamp(gaussCol.rgb / gaussCol.w, 0.0, 1.0); + gl_FragColor = vec4(gaussCol.rgb, alphaV); +} \ No newline at end of file diff --git a/src/three/shader/fragment/Chromatic.glsl b/src/three/shader/fragment/Chromatic.glsl new file mode 100644 index 0000000..413acd4 --- /dev/null +++ b/src/three/shader/fragment/Chromatic.glsl @@ -0,0 +1,20 @@ +varying vec2 vUv; + +uniform sampler2D tDiffuse; +uniform vec2 iResolution; +uniform float strength; + +vec4 ca(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { + vec4 col = vec4(0.0); + vec2 off = vec2(1.33333333333333) * direction; + col.ra = texture2D(image, uv).ra; + col.g = texture2D(image, uv - (off / resolution)).g; + col.b = texture2D(image, uv - 2. * (off / resolution)).b; + return col; +} + +void main() { + vec2 uv = gl_FragCoord.xy / iResolution; + vec2 direction = (uv - .5) * strength; + gl_FragColor = ca(tDiffuse, uv, iResolution.xy, direction); +} \ No newline at end of file diff --git a/src/three/shader/fragment/Copy.glsl b/src/three/shader/fragment/Copy.glsl new file mode 100644 index 0000000..1bcd4b8 --- /dev/null +++ b/src/three/shader/fragment/Copy.glsl @@ -0,0 +1,10 @@ +varying vec2 vUv; + +uniform float opacity; +uniform sampler2D tDiffuse; + +void main() { + + vec4 texel = texture2D(tDiffuse, vUv); + gl_FragColor = opacity * texel; +} \ No newline at end of file diff --git a/src/three/shader/fragment/FXAA.glsl b/src/three/shader/fragment/FXAA.glsl new file mode 100644 index 0000000..ee0b068 --- /dev/null +++ b/src/three/shader/fragment/FXAA.glsl @@ -0,0 +1,811 @@ +varying vec2 vUv; + +uniform sampler2D tDiffuse; +uniform vec2 resolution; + +// FXAA 3.11 implementation by NVIDIA, ported to WebGL by Agost Biro (biro@archilogic.com) +//-------------- +// File: es3-keplerFXAAassetsshaders/FXAA_DefaultES.frag +// SDK Version: v3.00 +// Email: gameworks@nvidia.com +// Site: http://developer.nvidia.com/ +// +// Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//-------------- + +#define FXAA_GLSL_100 1 +#define FXAA_QUALITY_PRESET 12 +#define FXAA_GREEN_AS_LUMA 1 + +/* ===== */ +#ifndef FXAA_PC_CONSOLE + #define FXAA_PC_CONSOLE 0 +#endif +/* ===== */ +#ifndef FXAA_GLSL_120 + #define FXAA_GLSL_120 0 +#endif +/* ===== */ +#ifndef FXAA_GLSL_130 + #define FXAA_GLSL_130 0 +#endif +/* ===== */ +#ifndef FXAA_HLSL_3 + #define FXAA_HLSL_3 0 +#endif +/* ===== */ +#ifndef FXAA_HLSL_4 + #define FXAA_HLSL_4 0 +#endif +/* ===== */ +#ifndef FXAA_HLSL_5 + #define FXAA_HLSL_5 0 +#endif +/* ========== */ +#ifndef FXAA_GREEN_AS_LUMA + #define FXAA_GREEN_AS_LUMA 0 +#endif +/* ===== */ +#ifndef FXAA_EARLY_EXIT + #define FXAA_EARLY_EXIT 1 +#endif + /* ===== */ +#ifndef FXAA_DISCARD + #define FXAA_DISCARD 0 +#endif +/* ===== */ +#ifndef FXAA_FAST_PIXEL_OFFSET + #ifdef GL_EXT_gpu_shader4 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifndef FXAA_FAST_PIXEL_OFFSET + #define FXAA_FAST_PIXEL_OFFSET 0 + #endif +#endif +/* ===== */ +#ifndef FXAA_GATHER4_ALPHA + #if (FXAA_HLSL_5 == 1) + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifndef FXAA_GATHER4_ALPHA + #define FXAA_GATHER4_ALPHA 0 + #endif +#endif + +/*========== +FXAA QUALITY - TUNING KNOBS +---------- +NOTE the other tuning knobs are now in the shader function inputs! +==========*/ +#ifndef FXAA_QUALITY_PRESET + #define FXAA_QUALITY_PRESET 12 +#endif + +/*========== FXAA QUALITY - PRESETS ==========*/ + +/*========== FXAA QUALITY - MEDIUM DITHER PRESETS ==========*/ +#if (FXAA_QUALITY_PRESET == 10) + #define FXAA_QUALITY_PS 3 + #define FXAA_QUALITY_P0 1.5 + #define FXAA_QUALITY_P1 3.0 + #define FXAA_QUALITY_P2 12.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 11) + #define FXAA_QUALITY_PS 4 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 3.0 + #define FXAA_QUALITY_P3 12.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 12) + #define FXAA_QUALITY_PS 5 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 4.0 + #define FXAA_QUALITY_P4 12.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 13) + #define FXAA_QUALITY_PS 6 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 4.0 + #define FXAA_QUALITY_P5 12.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 14) + #define FXAA_QUALITY_PS 7 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 4.0 + #define FXAA_QUALITY_P6 12.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 15) + #define FXAA_QUALITY_PS 8 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 4.0 + #define FXAA_QUALITY_P7 12.0 +#endif + + +/*========== FXAA QUALITY - LOW DITHER PRESETS ==========*/ + +#if (FXAA_QUALITY_PRESET == 20) + #define FXAA_QUALITY_PS 3 + #define FXAA_QUALITY_P0 1.5 + #define FXAA_QUALITY_P1 2.0 + #define FXAA_QUALITY_P2 8.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 21) + #define FXAA_QUALITY_PS 4 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 8.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 22) + #define FXAA_QUALITY_PS 5 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 8.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 23) + #define FXAA_QUALITY_PS 6 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 8.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 24) + #define FXAA_QUALITY_PS 7 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 3.0 + #define FXAA_QUALITY_P6 8.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 25) + #define FXAA_QUALITY_PS 8 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 4.0 + #define FXAA_QUALITY_P7 8.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 26) + #define FXAA_QUALITY_PS 9 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 4.0 + #define FXAA_QUALITY_P8 8.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 27) + #define FXAA_QUALITY_PS 10 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 4.0 + #define FXAA_QUALITY_P9 8.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 28) + #define FXAA_QUALITY_PS 11 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 4.0 + #define FXAA_QUALITY_P10 8.0 +#endif +/* ===== */ +#if (FXAA_QUALITY_PRESET == 29) + #define FXAA_QUALITY_PS 12 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.5 + #define FXAA_QUALITY_P2 2.0 + #define FXAA_QUALITY_P3 2.0 + #define FXAA_QUALITY_P4 2.0 + #define FXAA_QUALITY_P5 2.0 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 2.0 + #define FXAA_QUALITY_P10 4.0 + #define FXAA_QUALITY_P11 8.0 +#endif + +/*========== FXAA QUALITY - EXTREME QUALITY ==========*/ + +#if (FXAA_QUALITY_PRESET == 39) + #define FXAA_QUALITY_PS 12 + #define FXAA_QUALITY_P0 1.0 + #define FXAA_QUALITY_P1 1.0 + #define FXAA_QUALITY_P2 1.0 + #define FXAA_QUALITY_P3 1.0 + #define FXAA_QUALITY_P4 1.0 + #define FXAA_QUALITY_P5 1.5 + #define FXAA_QUALITY_P6 2.0 + #define FXAA_QUALITY_P7 2.0 + #define FXAA_QUALITY_P8 2.0 + #define FXAA_QUALITY_P9 2.0 + #define FXAA_QUALITY_P10 4.0 + #define FXAA_QUALITY_P11 8.0 +#endif + + +/*========== API PORTING ==========*/ + +#if (FXAA_GLSL_100 == 1) || (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) + #define FxaaBool bool + #define FxaaDiscard discard + #define FxaaFloat float + #define FxaaFloat2 vec2 + #define FxaaFloat3 vec3 + #define FxaaFloat4 vec4 + #define FxaaHalf float + #define FxaaHalf2 vec2 + #define FxaaHalf3 vec3 + #define FxaaHalf4 vec4 + #define FxaaInt2 ivec2 + #define FxaaSat(x) clamp(x, 0.0, 1.0) + #define FxaaTex sampler2D +#else + #define FxaaBool bool + #define FxaaDiscard clip(-1) + #define FxaaFloat float + #define FxaaFloat2 float2 + #define FxaaFloat3 float3 + #define FxaaFloat4 float4 + #define FxaaHalf half + #define FxaaHalf2 half2 + #define FxaaHalf3 half3 + #define FxaaHalf4 half4 + #define FxaaSat(x) saturate(x) +#endif +/* ===== */ +#if (FXAA_GLSL_100 == 1) + #define FxaaTexTop(t, p) texture2D(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r), 0.0) +#endif +/* ===== */ +#if (FXAA_GLSL_120 == 1) + #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) + #if (FXAA_FAST_PIXEL_OFFSET == 1) + #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) + #else + #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) + #endif + #if (FXAA_GATHER4_ALPHA == 1) + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/* ===== */ +#if (FXAA_GLSL_130 == 1) + #define FxaaTexTop(t, p) textureLod(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + #if (FXAA_GATHER4_ALPHA == 1) + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif + +/* ===== */ +#if (FXAA_HLSL_3 == 1) + #define FxaaInt2 float2 + #define FxaaTex sampler2D + #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0)) + #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0)) +#endif +/* ===== */ +#if (FXAA_HLSL_4 == 1) + #define FxaaInt2 int2 + + struct FxaaTex { + SamplerState smpl; + Texture2D tex; + }; + + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) +#endif +/* ===== */ +#if (FXAA_HLSL_5 == 1) + #define FxaaInt2 int2 + + struct FxaaTex { + SamplerState smpl; + Texture2D tex; + }; + + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) + #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) + #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) + #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) + #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) +#endif + + +/*========== GREEN AS LUMA OPTION SUPPORT FUNCTION ==========*/ + +#if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { + return rgba.w; + } + #else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { + return rgba.y; + } +#endif + + +/*========== FXAA3 - PC ==========*/ + +FxaaFloat4 FxaaPixelShader(FxaaFloat2 pos, FxaaFloat4 fxaaConsolePosPos, FxaaTex tex, FxaaTex fxaaConsole360TexExpBiasNegOne, FxaaTex fxaaConsole360TexExpBiasNegTwo, FxaaFloat2 fxaaQualityRcpFrame, FxaaFloat4 fxaaConsoleRcpFrameOpt, FxaaFloat4 fxaaConsoleRcpFrameOpt2, FxaaFloat4 fxaaConsole360RcpFrameOpt2, FxaaFloat fxaaQualitySubpix, FxaaFloat fxaaQualityEdgeThreshold, FxaaFloat fxaaQualityEdgeThresholdMin, FxaaFloat fxaaConsoleEdgeSharpness, FxaaFloat fxaaConsoleEdgeThreshold, FxaaFloat fxaaConsoleEdgeThresholdMin, FxaaFloat4 fxaaConsole360ConstDir) { + /* ===== */ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #if (FXAA_GLSL_100 == 1) + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(0.0, 1.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(1.0, 0.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(0.0, -1.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0, 0.0), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(0, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif + #endif + /* ===== */ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; + /* ===== */ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif + /* ===== */ + #if (FXAA_GATHER4_ALPHA == 0) + #if (FXAA_GLSL_100 == 1) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0, -1.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(1.0, 1.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(1.0, -1.0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0, 1.0), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif + /* ===== */ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0 / range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; + /* ===== */ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; + /* ===== */ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; + /* ===== */ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; + /* ===== */ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0 / 12.0)) - lumaM; + /* ===== */ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); + /* ===== */ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = (horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if(horzSpan) posB.y += lengthSign * 0.5; + /* ===== */ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY_P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY_P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY_P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY_P0; + FxaaFloat subpixD = ((-2.0) * subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); + /* ===== */ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0 / 4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; + /* ===== */ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P1; + /* ===== */ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P2; + /* ===== */ + #if (FXAA_QUALITY_PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P3; + /* ===== */ + #if (FXAA_QUALITY_PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P4; + /* ===== */ + #if (FXAA_QUALITY_PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P5; + /* ===== */ + #if (FXAA_QUALITY_PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P6; + /* ===== */ + #if (FXAA_QUALITY_PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P7; + /* ===== */ + #if (FXAA_QUALITY_PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P8; + /* ===== */ + #if (FXAA_QUALITY_PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P9; + /* ===== */ + #if (FXAA_QUALITY_PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P10; + /* ===== */ + #if (FXAA_QUALITY_PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P11; + /* ===== */ + #if (FXAA_QUALITY_PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P12; + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + #endif + /* ===== */ + } + /* ===== */ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; + /* ===== */ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0 / spanLength; + /* ===== */ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; + /* ===== */ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if(horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + #endif +} +/* ========== */ + +void main() { + gl_FragColor = FxaaPixelShader(vUv, vec4(0.0), tDiffuse, tDiffuse, tDiffuse, resolution, vec4(0.0), vec4(0.0), vec4(0.0), 0.75, 0.166, 0.0833, 0.0, 0.0, 0.0, vec4(0.0)); + gl_FragColor.a = texture2D(tDiffuse, vUv).a; +} \ No newline at end of file diff --git a/src/three/shader/fragment/FractalMirror.glsl b/src/three/shader/fragment/FractalMirror.glsl new file mode 100644 index 0000000..1217531 --- /dev/null +++ b/src/three/shader/fragment/FractalMirror.glsl @@ -0,0 +1,44 @@ +uniform sampler2D tDiffuse; +uniform vec2 iResolution; +uniform float numSides; +uniform bool invert; + +varying vec2 vUv; + +const float PI = 3.14159265358979323846; + +void main() { + vec2 center = vec2(0.5, 0.5); + float zoom = iResolution.x / iResolution.y; + vec2 uv = center - vUv; + + if(zoom > 1.0) { + uv.y /= zoom; + } + else { + uv.x *= zoom; + } + + + float KA = PI / numSides; + float angle = abs(mod(atan(uv.y, uv.x), 2.0 * KA) - KA); + + if(zoom > 1.0) { + angle -= 45.0; + } + + vec2 transformed = length(uv) * vec2(sin(angle), cos(angle)); + if(!invert) { + transformed += center; + } + else { + if(transformed.x < 0.0) { + transformed.x += 1.0; + } + if(transformed.y < 0.0) { + transformed.y += 1.0; + } + } + + gl_FragColor = texture2D(tDiffuse, transformed); +} \ No newline at end of file diff --git a/src/three/shader/fragment/LUT.glsl b/src/three/shader/fragment/LUT.glsl new file mode 100644 index 0000000..cf1a0b1 --- /dev/null +++ b/src/three/shader/fragment/LUT.glsl @@ -0,0 +1,38 @@ +#include + +#define FILTER_LUT true + +varying vec2 vUv; + +uniform sampler2D tDiffuse; +uniform sampler2D lutMap; +uniform float lutMapSize; + +vec4 sampleAs3DTexture(sampler2D tex, vec3 texCoord, float size) { + float sliceSize = 1.0 / size; // space of 1 slice + float slicePixelSize = sliceSize / size; // space of 1 pixel + float width = size - 1.0; + float sliceInnerSize = slicePixelSize * width; // space of size pixels + float zSlice0 = floor(texCoord.z * width); + float zSlice1 = min(zSlice0 + 1.0, width); + float xOffset = slicePixelSize * 0.5 + texCoord.x * sliceInnerSize; + float yRange = (texCoord.y * width + 0.5) / size; + float s0 = xOffset + (zSlice0 * sliceSize); + + #ifdef FILTER_LUT + float s1 = xOffset + (zSlice1 * sliceSize); + vec4 slice0Color = texture2D(tex, vec2(s0, yRange)); + vec4 slice1Color = texture2D(tex, vec2(s1, yRange)); + float zOffset = mod(texCoord.z * width, 1.0); + return mix(slice0Color, slice1Color, zOffset); + #else + return texture2D(tex, vec2(s0, yRange)); + #endif +} + +void main() { + vec4 originalColor = texture2D(tDiffuse, vUv); + vec4 tempColor = sampleAs3DTexture(lutMap, originalColor.xyz, lutMapSize); + tempColor.a = originalColor.a; + gl_FragColor = tempColor; +} \ No newline at end of file diff --git a/src/three/shader/fragment/Luminosity.glsl b/src/three/shader/fragment/Luminosity.glsl new file mode 100644 index 0000000..b31ea7f --- /dev/null +++ b/src/three/shader/fragment/Luminosity.glsl @@ -0,0 +1,16 @@ +varying vec2 vUv; + +uniform sampler2D tDiffuse; +uniform vec3 defaultColor; +uniform float defaultOpacity; +uniform float luminosityThreshold; +uniform float smoothWidth; + +void main() { + vec4 texel = texture2D(tDiffuse, vUv); + vec3 luma = vec3(0.299, 0.587, 0.114); + float v = dot(texel.xyz, luma); + vec4 outputColor = vec4(defaultColor.rgb, defaultOpacity); + float alpha = smoothstep(luminosityThreshold, luminosityThreshold + smoothWidth, v); + gl_FragColor = mix(outputColor, texel, alpha); +} \ No newline at end of file diff --git a/src/three/shader/loader/glsl.d.ts b/src/three/shader/loader/glsl.d.ts new file mode 100644 index 0000000..4846ef1 --- /dev/null +++ b/src/three/shader/loader/glsl.d.ts @@ -0,0 +1,4 @@ +declare module '*.glsl' { + const value: string; + export default value; +} diff --git a/src/three/shader/loader/index.js b/src/three/shader/loader/index.js new file mode 100644 index 0000000..66e20b6 --- /dev/null +++ b/src/three/shader/loader/index.js @@ -0,0 +1,111 @@ +const fs = require('fs'); +const path = require('path'); + +/** + * + * @param loader + * @param source + * @param context + * @param cb + */ +function parse(loader, source, context, cb) { + const imports = []; + const importPattern = /@import ([.\/\w_-]+);/gi; + let match = importPattern.exec(source); + + while (match != null) { + imports.push({ + key: match[1], + target: match[0], + content: '', + }); + match = importPattern.exec(source); + } + + processImports(loader, source, context, imports, cb); +} + +/** + * + * @param loader + * @param source + * @param context + * @param imports + * @param cb + * @returns + */ +function processImports(loader, source, context, imports, cb) { + // if no imports left, resolve + if (imports.length === 0) { + return cb(null, source); + } + + const imp = imports.pop(); + + loader.resolve(context, `${imp.key}.glsl`, (err, resolved) => { + if (err) { + return cb(err); + } + + loader.addDependency(resolved); + fs.readFile(resolved, 'utf-8', (err, src) => { + if (err) { + return cb(err); + } + + parse(loader, src, path.dirname(resolved), (err, bld) => { + if (err) { + return cb(err); + } + + const newSource = source.replace(imp.target, bld); + + // call all imports recursively + processImports(loader, newSource, context, imports, cb); + }); + }); + }); +} + +/** + * @todo find fix for #define and #if etc + * Remove comments, newlines, tabstops & unnecessary spaces + * @param src + */ +function optimize(src) { + // src = src.replaceAll(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1'); + const customs = ['+', '-', '/', '*', '=', `==`, '!=', '>=', '<=', '===', '>', '<', ',', '+=', '-=', '*=', '/=', '(', ')', '{', '}']; + let spc = src.replaceAll(' ', ' '); + for (let i = 0; i < customs.length; i++) { + const ci = customs[i]; + // @todo improve + // ugly special case for #incude stuffs + if (ci.length == 1) spc = spc.replaceAll(' ' + ci + ' ', ci); + else spc = spc.replaceAll(' ' + ci, ci).replaceAll(ci + ' ', ci); + } + // @todo macke this better + // all (new)lines starting with '#' and the following ones need to stay! + if (spc.indexOf('#') < 0) { + spc = spc.replaceAll(/[\r\n]/, ''); + } + return spc.replaceAll(/[\t]/, ''); +} + +/** + * + * @param source + */ +exports.default = function(source) { + this.cacheable(); + const cb = this.async(); + + parse(this, source, this.context, (err, bld) => { + if (err) return cb(err); + + // do minifying + const repl = optimize(bld); + console.log('[GLSLoader] Shortened program by: ' + (bld.length - repl.length) + ' chars'); + + cb(null, `export default ${JSON.stringify(repl)}`); + }); +}; diff --git a/src/three/shader/vertex/Basic.glsl b/src/three/shader/vertex/Basic.glsl new file mode 100644 index 0000000..fb20d32 --- /dev/null +++ b/src/three/shader/vertex/Basic.glsl @@ -0,0 +1,6 @@ +varying vec2 vUv; + +void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); +} \ No newline at end of file From 307ea1d3f9f05af59f2b73c2e8798bf39e8b10ae Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Mon, 18 Oct 2021 12:49:42 +0200 Subject: [PATCH 37/76] switch to WebPack shader loader --- src/three/shader/BlendShader.ts | 26 +- src/three/shader/BlurShader.ts | 48 +- src/three/shader/ChromaticShader.ts | 35 +- src/three/shader/CopyShader.ts | 25 +- src/three/shader/FXAAShader.ts | 849 +------------------ src/three/shader/FractalMirrorShader.ts | 42 +- src/three/shader/LUTShader.ts | 56 +- src/three/shader/LUTShaderNearest.ts | 2 +- src/three/shader/LuminosityHighPassShader.ts | 32 +- 9 files changed, 41 insertions(+), 1074 deletions(-) diff --git a/src/three/shader/BlendShader.ts b/src/three/shader/BlendShader.ts index 2b04ab4..809c98e 100644 --- a/src/three/shader/BlendShader.ts +++ b/src/three/shader/BlendShader.ts @@ -9,6 +9,9 @@ import {BaseShader} from './BaseShader'; +import vertex from './vertex/Basic.glsl'; +import fragment from './fragment/Blend.glsl'; + /** * Blend another texture in and out * @public @@ -25,26 +28,7 @@ export class BlendShader implements BaseShader { mixValue: {value: 1}, }; - vertexShader = ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `; + vertexShader = vertex; - fragmentShader = ` - uniform sampler2D tDiffuse; - uniform sampler2D overlayBuffer; - - varying vec2 vUv; - - void main() { - vec4 texel1 = texture2D(tDiffuse, vUv); - vec4 texel2 = texture2D(overlayBuffer, vUv); - vec4 diff = abs(texel1 - texel2); - gl_FragColor = vec4(diff, 1.0); - } - `; + fragmentShader = fragment; } diff --git a/src/three/shader/BlurShader.ts b/src/three/shader/BlurShader.ts index 5496978..c4f451f 100644 --- a/src/three/shader/BlurShader.ts +++ b/src/three/shader/BlurShader.ts @@ -10,6 +10,9 @@ import {Vector2} from 'three'; import {BaseShader} from './BaseShader'; +import vertex from './vertex/Basic.glsl'; +import fragment from './fragment/Blur.glsl'; + /** * Blur shader with Alpha support * @public @@ -27,48 +30,7 @@ export class BlurShader implements BaseShader { u_dir: {value: new Vector2(0.1, 0.1)}, }; - vertexShader = ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - } - `; + vertexShader = vertex; - fragmentShader = ` - varying vec2 vUv; - - uniform sampler2D tDiffuse; - uniform vec2 iResolution; - uniform float u_sigma; - uniform vec2 u_dir; - - float CalcGauss(float x, float sigma) { - if ( sigma <= 0.0 ) return 0.0; - return exp( -(x*x) / (2.0 * sigma) ) / (2.0 * 3.14157 * sigma); - } - - void main() { - vec2 texC = vUv; - vec4 texCol = texture2D( tDiffuse, texC ); - vec4 gaussCol = vec4( texCol.rgb, 1.0 ); - float alphaV = texCol.a; - vec2 step = u_dir / iResolution; - for (int i = 1; i <= 32; ++ i) - { - float weight = CalcGauss(float(i) / 32.0, u_sigma * 0.5); - if (weight < 1.0/255.0) break; - texCol = texture2D(tDiffuse, texC + step * float(i)); - gaussCol += vec4(texCol.rgb * weight, weight); - alphaV += texCol.a * weight; - texCol = texture2D(tDiffuse, texC - step * float(i)); - gaussCol += vec4(texCol.rgb * weight, weight); - alphaV += texCol.a * weight; - } - alphaV = clamp(alphaV / gaussCol.w, 0.0, 1.0); - gaussCol.rgb = clamp(gaussCol.rgb / gaussCol.w, 0.0, 1.0); - gl_FragColor = vec4(gaussCol.rgb, alphaV); - } - `; + fragmentShader = fragment; } diff --git a/src/three/shader/ChromaticShader.ts b/src/three/shader/ChromaticShader.ts index ee39998..c0ccf97 100644 --- a/src/three/shader/ChromaticShader.ts +++ b/src/three/shader/ChromaticShader.ts @@ -10,6 +10,9 @@ import {Vector2} from 'three'; import {BaseShader} from './BaseShader'; +import vertex from './vertex/Basic.glsl'; +import fragment from './fragment/Chromatic.glsl'; + /** * Chromatic Abberation shader with alpha support * @public @@ -26,35 +29,7 @@ export class ChromaticShader implements BaseShader { strength: {value: 10.0}, }; - vertexShader = ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - } - `; + vertexShader = vertex; - fragmentShader = ` - uniform sampler2D tDiffuse; - uniform vec2 iResolution; - uniform float strength; - - varying vec2 vUv; - - vec4 ca(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { - vec4 col = vec4(0.0); - vec2 off = vec2(1.33333333333333) * direction; - col.ra = texture2D(image, uv).ra; - col.g = texture2D(image, uv - (off / resolution)).g; - col.b = texture2D(image, uv - 2. * (off / resolution)).b; - return col; - } - - void main() { - vec2 uv = gl_FragCoord.xy / iResolution; - vec2 direction = (uv - .5) * strength; - gl_FragColor = ca(tDiffuse, uv, iResolution.xy, direction); - } - `; + fragmentShader = fragment; }; diff --git a/src/three/shader/CopyShader.ts b/src/three/shader/CopyShader.ts index 300c8f5..4629c06 100644 --- a/src/three/shader/CopyShader.ts +++ b/src/three/shader/CopyShader.ts @@ -10,6 +10,9 @@ import {BaseShader} from './BaseShader'; +import vertex from './vertex/Basic.glsl'; +import fragment from './fragment/Copy.glsl'; + /** * Siimple I/O shader * @public @@ -25,25 +28,7 @@ export class CopyShader implements BaseShader { opacity: {value: 1.0}, } - vertexShader = ` - varying vec2 vUv; - - void main() { - - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - } - `; + vertexShader = vertex; - fragmentShader = ` - uniform float opacity; - uniform sampler2D tDiffuse; - varying vec2 vUv; - - void main() { - - vec4 texel = texture2D( tDiffuse, vUv ); - gl_FragColor = opacity * texel; - } - `; + fragmentShader = fragment; } diff --git a/src/three/shader/FXAAShader.ts b/src/three/shader/FXAAShader.ts index f6f1150..1bc269f 100644 --- a/src/three/shader/FXAAShader.ts +++ b/src/three/shader/FXAAShader.ts @@ -7,6 +7,9 @@ import {Vector2} from 'three'; import {BaseShader} from './BaseShader'; +import vertex from './vertex/Basic.glsl'; +import fragment from './fragment/FXAA.glsl'; + /** * NVIDIA FXAA by Timothy Lottes * http://timothylottes.blogspot.com/2011/06/fxaa3-source-released.html @@ -26,851 +29,9 @@ export class FXAAShader implements BaseShader { resolution: {value: new Vector2(1 / 1024, 1 / 512)}, } - vertexShader = ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - } - `; + vertexShader = vertex; - fragmentShader = ` - - uniform sampler2D tDiffuse; - uniform vec2 resolution; - varying vec2 vUv; - - // FXAA 3.11 implementation by NVIDIA, ported to WebGL by Agost Biro (biro@archilogic.com) - //-------------- - // File: es3-keplerFXAAassetsshaders/FXAA_DefaultES.frag - // SDK Version: v3.00 - // Email: gameworks@nvidia.com - // Site: http://developer.nvidia.com/ - // - // Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. - // - // Redistribution and use in source and binary forms, with or without - // modification, are permitted provided that the following conditions - // are met: - // * Redistributions of source code must retain the above copyright - // notice, this list of conditions and the following disclaimer. - // * Redistributions in binary form must reproduce the above copyright - // notice, this list of conditions and the following disclaimer in the - // documentation and/or other materials provided with the distribution. - // * Neither the name of NVIDIA CORPORATION nor the names of its - // contributors may be used to endorse or promote products derived - // from this software without specific prior written permission. - // - // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY - // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - // - //-------------- - - #define FXAA_PC 1 - #define FXAA_GLSL_100 1 - #define FXAA_QUALITY_PRESET 12 - #define FXAA_GREEN_AS_LUMA 1 - /* ===== */ - #ifndef FXAA_PC_CONSOLE - #define FXAA_PC_CONSOLE 0 - #endif - /* ===== */ - #ifndef FXAA_GLSL_120 - #define FXAA_GLSL_120 0 - #endif - /* ===== */ - #ifndef FXAA_GLSL_130 - #define FXAA_GLSL_130 0 - #endif - /* ===== */ - #ifndef FXAA_HLSL_3 - #define FXAA_HLSL_3 0 - #endif - /* ===== */ - #ifndef FXAA_HLSL_4 - #define FXAA_HLSL_4 0 - #endif - /* ===== */ - #ifndef FXAA_HLSL_5 - #define FXAA_HLSL_5 0 - #endif - /* ========== */ - #ifndef FXAA_GREEN_AS_LUMA - #define FXAA_GREEN_AS_LUMA 0 - #endif - /* ===== */ - #ifndef FXAA_EARLY_EXIT - #define FXAA_EARLY_EXIT 1 - #endif - /* ===== */ - #ifndef FXAA_DISCARD - #define FXAA_DISCARD 0 - #endif - /* ===== */ - #ifndef FXAA_FAST_PIXEL_OFFSET - #ifdef GL_EXT_gpu_shader4 - #define FXAA_FAST_PIXEL_OFFSET 1 - #endif - #ifdef GL_NV_gpu_shader5 - #define FXAA_FAST_PIXEL_OFFSET 1 - #endif - #ifdef GL_ARB_gpu_shader5 - #define FXAA_FAST_PIXEL_OFFSET 1 - #endif - #ifndef FXAA_FAST_PIXEL_OFFSET - #define FXAA_FAST_PIXEL_OFFSET 0 - #endif - #endif - /* ===== */ - #ifndef FXAA_GATHER4_ALPHA - #if (FXAA_HLSL_5 == 1) - #define FXAA_GATHER4_ALPHA 1 - #endif - #ifdef GL_ARB_gpu_shader5 - #define FXAA_GATHER4_ALPHA 1 - #endif - #ifdef GL_NV_gpu_shader5 - #define FXAA_GATHER4_ALPHA 1 - #endif - #ifndef FXAA_GATHER4_ALPHA - #define FXAA_GATHER4_ALPHA 0 - #endif - #endif - - /*========== - FXAA QUALITY - TUNING KNOBS - ---------- - NOTE the other tuning knobs are now in the shader function inputs! - ==========*/ - #ifndef FXAA_QUALITY_PRESET - #define FXAA_QUALITY_PRESET 12 - #endif - - /*========== - FXAA QUALITY - PRESETS - ==========*/ - /*========== - FXAA QUALITY - MEDIUM DITHER PRESETS - ==========*/ - #if (FXAA_QUALITY_PRESET == 10) - #define FXAA_QUALITY_PS 3 - #define FXAA_QUALITY_P0 1.5 - #define FXAA_QUALITY_P1 3.0 - #define FXAA_QUALITY_P2 12.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 11) - #define FXAA_QUALITY_PS 4 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 3.0 - #define FXAA_QUALITY_P3 12.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 12) - #define FXAA_QUALITY_PS 5 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 4.0 - #define FXAA_QUALITY_P4 12.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 13) - #define FXAA_QUALITY_PS 6 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 2.0 - #define FXAA_QUALITY_P4 4.0 - #define FXAA_QUALITY_P5 12.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 14) - #define FXAA_QUALITY_PS 7 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 2.0 - #define FXAA_QUALITY_P4 2.0 - #define FXAA_QUALITY_P5 4.0 - #define FXAA_QUALITY_P6 12.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 15) - #define FXAA_QUALITY_PS 8 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 2.0 - #define FXAA_QUALITY_P4 2.0 - #define FXAA_QUALITY_P5 2.0 - #define FXAA_QUALITY_P6 4.0 - #define FXAA_QUALITY_P7 12.0 - #endif - /*========== - FXAA QUALITY - LOW DITHER PRESETS - ==========*/ - #if (FXAA_QUALITY_PRESET == 20) - #define FXAA_QUALITY_PS 3 - #define FXAA_QUALITY_P0 1.5 - #define FXAA_QUALITY_P1 2.0 - #define FXAA_QUALITY_P2 8.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 21) - #define FXAA_QUALITY_PS 4 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 8.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 22) - #define FXAA_QUALITY_PS 5 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 2.0 - #define FXAA_QUALITY_P4 8.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 23) - #define FXAA_QUALITY_PS 6 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 2.0 - #define FXAA_QUALITY_P4 2.0 - #define FXAA_QUALITY_P5 8.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 24) - #define FXAA_QUALITY_PS 7 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 2.0 - #define FXAA_QUALITY_P4 2.0 - #define FXAA_QUALITY_P5 3.0 - #define FXAA_QUALITY_P6 8.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 25) - #define FXAA_QUALITY_PS 8 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 2.0 - #define FXAA_QUALITY_P4 2.0 - #define FXAA_QUALITY_P5 2.0 - #define FXAA_QUALITY_P6 4.0 - #define FXAA_QUALITY_P7 8.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 26) - #define FXAA_QUALITY_PS 9 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 2.0 - #define FXAA_QUALITY_P4 2.0 - #define FXAA_QUALITY_P5 2.0 - #define FXAA_QUALITY_P6 2.0 - #define FXAA_QUALITY_P7 4.0 - #define FXAA_QUALITY_P8 8.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 27) - #define FXAA_QUALITY_PS 10 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 2.0 - #define FXAA_QUALITY_P4 2.0 - #define FXAA_QUALITY_P5 2.0 - #define FXAA_QUALITY_P6 2.0 - #define FXAA_QUALITY_P7 2.0 - #define FXAA_QUALITY_P8 4.0 - #define FXAA_QUALITY_P9 8.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 28) - #define FXAA_QUALITY_PS 11 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 2.0 - #define FXAA_QUALITY_P4 2.0 - #define FXAA_QUALITY_P5 2.0 - #define FXAA_QUALITY_P6 2.0 - #define FXAA_QUALITY_P7 2.0 - #define FXAA_QUALITY_P8 2.0 - #define FXAA_QUALITY_P9 4.0 - #define FXAA_QUALITY_P10 8.0 - #endif - /* ===== */ - #if (FXAA_QUALITY_PRESET == 29) - #define FXAA_QUALITY_PS 12 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.5 - #define FXAA_QUALITY_P2 2.0 - #define FXAA_QUALITY_P3 2.0 - #define FXAA_QUALITY_P4 2.0 - #define FXAA_QUALITY_P5 2.0 - #define FXAA_QUALITY_P6 2.0 - #define FXAA_QUALITY_P7 2.0 - #define FXAA_QUALITY_P8 2.0 - #define FXAA_QUALITY_P9 2.0 - #define FXAA_QUALITY_P10 4.0 - #define FXAA_QUALITY_P11 8.0 - #endif - /*========== - FXAA QUALITY - EXTREME QUALITY - ==========*/ - #if (FXAA_QUALITY_PRESET == 39) - #define FXAA_QUALITY_PS 12 - #define FXAA_QUALITY_P0 1.0 - #define FXAA_QUALITY_P1 1.0 - #define FXAA_QUALITY_P2 1.0 - #define FXAA_QUALITY_P3 1.0 - #define FXAA_QUALITY_P4 1.0 - #define FXAA_QUALITY_P5 1.5 - #define FXAA_QUALITY_P6 2.0 - #define FXAA_QUALITY_P7 2.0 - #define FXAA_QUALITY_P8 2.0 - #define FXAA_QUALITY_P9 2.0 - #define FXAA_QUALITY_P10 4.0 - #define FXAA_QUALITY_P11 8.0 - #endif - - /*========== - API PORTING - ==========*/ - #if (FXAA_GLSL_100 == 1) || (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) - #define FxaaBool bool - #define FxaaDiscard discard - #define FxaaFloat float - #define FxaaFloat2 vec2 - #define FxaaFloat3 vec3 - #define FxaaFloat4 vec4 - #define FxaaHalf float - #define FxaaHalf2 vec2 - #define FxaaHalf3 vec3 - #define FxaaHalf4 vec4 - #define FxaaInt2 ivec2 - #define FxaaSat(x) clamp(x, 0.0, 1.0) - #define FxaaTex sampler2D - #else - #define FxaaBool bool - #define FxaaDiscard clip(-1) - #define FxaaFloat float - #define FxaaFloat2 float2 - #define FxaaFloat3 float3 - #define FxaaFloat4 float4 - #define FxaaHalf half - #define FxaaHalf2 half2 - #define FxaaHalf3 half3 - #define FxaaHalf4 half4 - #define FxaaSat(x) saturate(x) - #endif - /* ===== */ - #if (FXAA_GLSL_100 == 1) - #define FxaaTexTop(t, p) texture2D(t, p, 0.0) - #define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r), 0.0) - #endif - /* ===== */ - #if (FXAA_GLSL_120 == 1) - #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) - #if (FXAA_FAST_PIXEL_OFFSET == 1) - #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) - #else - #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) - #endif - #if (FXAA_GATHER4_ALPHA == 1) - #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) - #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) - #define FxaaTexGreen4(t, p) textureGather(t, p, 1) - #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) - #endif - #endif - /* ===== */ - #if (FXAA_GLSL_130 == 1) - #define FxaaTexTop(t, p) textureLod(t, p, 0.0) - #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) - #if (FXAA_GATHER4_ALPHA == 1) - #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) - #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) - #define FxaaTexGreen4(t, p) textureGather(t, p, 1) - #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) - #endif - #endif - /* ===== */ - #if (FXAA_HLSL_3 == 1) - #define FxaaInt2 float2 - #define FxaaTex sampler2D - #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0)) - #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0)) - #endif - /* ===== */ - #if (FXAA_HLSL_4 == 1) - #define FxaaInt2 int2 - struct FxaaTex { SamplerState smpl; Texture2D tex; }; - #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) - #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) - #endif - /* ===== */ - #if (FXAA_HLSL_5 == 1) - #define FxaaInt2 int2 - struct FxaaTex { SamplerState smpl; Texture2D tex; }; - #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) - #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) - #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) - #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) - #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) - #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) - #endif - - /*========== - GREEN AS LUMA OPTION SUPPORT FUNCTION - ==========*/ - #if (FXAA_GREEN_AS_LUMA == 0) - FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } - #else - FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } - #endif - - - /*========== - FXAA3 QUALITY - PC - ==========*/ - #if (FXAA_PC == 1) - /* ===== */ - FxaaFloat4 FxaaPixelShader( - FxaaFloat2 pos, - FxaaFloat4 fxaaConsolePosPos, - FxaaTex tex, - FxaaTex fxaaConsole360TexExpBiasNegOne, - FxaaTex fxaaConsole360TexExpBiasNegTwo, - FxaaFloat2 fxaaQualityRcpFrame, - FxaaFloat4 fxaaConsoleRcpFrameOpt, - FxaaFloat4 fxaaConsoleRcpFrameOpt2, - FxaaFloat4 fxaaConsole360RcpFrameOpt2, - FxaaFloat fxaaQualitySubpix, - FxaaFloat fxaaQualityEdgeThreshold, - FxaaFloat fxaaQualityEdgeThresholdMin, - FxaaFloat fxaaConsoleEdgeSharpness, - FxaaFloat fxaaConsoleEdgeThreshold, - FxaaFloat fxaaConsoleEdgeThresholdMin, - FxaaFloat4 fxaaConsole360ConstDir - ) { - /* ===== */ - FxaaFloat2 posM; - posM.x = pos.x; - posM.y = pos.y; - #if (FXAA_GATHER4_ALPHA == 1) - #if (FXAA_DISCARD == 0) - FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); - #if (FXAA_GREEN_AS_LUMA == 0) - #define lumaM rgbyM.w - #else - #define lumaM rgbyM.y - #endif - #endif - #if (FXAA_GREEN_AS_LUMA == 0) - FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); - FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); - #else - FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); - FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); - #endif - #if (FXAA_DISCARD == 1) - #define lumaM luma4A.w - #endif - #define lumaE luma4A.z - #define lumaS luma4A.x - #define lumaSE luma4A.y - #define lumaNW luma4B.w - #define lumaN luma4B.z - #define lumaW luma4B.x - #else - FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); - #if (FXAA_GREEN_AS_LUMA == 0) - #define lumaM rgbyM.w - #else - #define lumaM rgbyM.y - #endif - #if (FXAA_GLSL_100 == 1) - FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 0.0, 1.0), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0, 0.0), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 0.0,-1.0), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0, 0.0), fxaaQualityRcpFrame.xy)); - #else - FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); - #endif - #endif - /* ===== */ - FxaaFloat maxSM = max(lumaS, lumaM); - FxaaFloat minSM = min(lumaS, lumaM); - FxaaFloat maxESM = max(lumaE, maxSM); - FxaaFloat minESM = min(lumaE, minSM); - FxaaFloat maxWN = max(lumaN, lumaW); - FxaaFloat minWN = min(lumaN, lumaW); - FxaaFloat rangeMax = max(maxWN, maxESM); - FxaaFloat rangeMin = min(minWN, minESM); - FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; - FxaaFloat range = rangeMax - rangeMin; - FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); - FxaaBool earlyExit = range < rangeMaxClamped; - /* ===== */ - if(earlyExit) - #if (FXAA_DISCARD == 1) - FxaaDiscard; - #else - return rgbyM; - #endif - /* ===== */ - #if (FXAA_GATHER4_ALPHA == 0) - #if (FXAA_GLSL_100 == 1) - FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0,-1.0), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0, 1.0), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0,-1.0), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0, 1.0), fxaaQualityRcpFrame.xy)); - #else - FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); - #endif - #else - FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); - FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); - #endif - /* ===== */ - FxaaFloat lumaNS = lumaN + lumaS; - FxaaFloat lumaWE = lumaW + lumaE; - FxaaFloat subpixRcpRange = 1.0/range; - FxaaFloat subpixNSWE = lumaNS + lumaWE; - FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; - FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; - /* ===== */ - FxaaFloat lumaNESE = lumaNE + lumaSE; - FxaaFloat lumaNWNE = lumaNW + lumaNE; - FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; - FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; - /* ===== */ - FxaaFloat lumaNWSW = lumaNW + lumaSW; - FxaaFloat lumaSWSE = lumaSW + lumaSE; - FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); - FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); - FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; - FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; - FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; - FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; - /* ===== */ - FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; - FxaaFloat lengthSign = fxaaQualityRcpFrame.x; - FxaaBool horzSpan = edgeHorz >= edgeVert; - FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; - /* ===== */ - if(!horzSpan) lumaN = lumaW; - if(!horzSpan) lumaS = lumaE; - if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; - FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; - /* ===== */ - FxaaFloat gradientN = lumaN - lumaM; - FxaaFloat gradientS = lumaS - lumaM; - FxaaFloat lumaNN = lumaN + lumaM; - FxaaFloat lumaSS = lumaS + lumaM; - FxaaBool pairN = abs(gradientN) >= abs(gradientS); - FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); - if(pairN) lengthSign = -lengthSign; - FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); - /* ===== */ - FxaaFloat2 posB; - posB.x = posM.x; - posB.y = posM.y; - FxaaFloat2 offNP; - offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; - offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; - if(!horzSpan) posB.x += lengthSign * 0.5; - if( horzSpan) posB.y += lengthSign * 0.5; - /* ===== */ - FxaaFloat2 posN; - posN.x = posB.x - offNP.x * FXAA_QUALITY_P0; - posN.y = posB.y - offNP.y * FXAA_QUALITY_P0; - FxaaFloat2 posP; - posP.x = posB.x + offNP.x * FXAA_QUALITY_P0; - posP.y = posB.y + offNP.y * FXAA_QUALITY_P0; - FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; - FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); - FxaaFloat subpixE = subpixC * subpixC; - FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); - /* ===== */ - if(!pairN) lumaNN = lumaSS; - FxaaFloat gradientScaled = gradient * 1.0/4.0; - FxaaFloat lumaMM = lumaM - lumaNN * 0.5; - FxaaFloat subpixF = subpixD * subpixE; - FxaaBool lumaMLTZero = lumaMM < 0.0; - /* ===== */ - lumaEndN -= lumaNN * 0.5; - lumaEndP -= lumaNN * 0.5; - FxaaBool doneN = abs(lumaEndN) >= gradientScaled; - FxaaBool doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P1; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P1; - FxaaBool doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P1; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P1; - /* ===== */ - if(doneNP) { - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; - doneN = abs(lumaEndN) >= gradientScaled; - doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P2; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P2; - doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P2; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P2; - /* ===== */ - #if (FXAA_QUALITY_PS > 3) - if(doneNP) { - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; - doneN = abs(lumaEndN) >= gradientScaled; - doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P3; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P3; - doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P3; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P3; - /* ===== */ - #if (FXAA_QUALITY_PS > 4) - if(doneNP) { - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; - doneN = abs(lumaEndN) >= gradientScaled; - doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P4; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P4; - doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P4; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P4; - /* ===== */ - #if (FXAA_QUALITY_PS > 5) - if(doneNP) { - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; - doneN = abs(lumaEndN) >= gradientScaled; - doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P5; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P5; - doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P5; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P5; - /* ===== */ - #if (FXAA_QUALITY_PS > 6) - if(doneNP) { - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; - doneN = abs(lumaEndN) >= gradientScaled; - doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P6; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P6; - doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P6; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P6; - /* ===== */ - #if (FXAA_QUALITY_PS > 7) - if(doneNP) { - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; - doneN = abs(lumaEndN) >= gradientScaled; - doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P7; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P7; - doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P7; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P7; - /* ===== */ - #if (FXAA_QUALITY_PS > 8) - if(doneNP) { - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; - doneN = abs(lumaEndN) >= gradientScaled; - doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P8; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P8; - doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P8; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P8; - /* ===== */ - #if (FXAA_QUALITY_PS > 9) - if(doneNP) { - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; - doneN = abs(lumaEndN) >= gradientScaled; - doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P9; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P9; - doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P9; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P9; - /* ===== */ - #if (FXAA_QUALITY_PS > 10) - if(doneNP) { - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; - doneN = abs(lumaEndN) >= gradientScaled; - doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P10; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P10; - doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P10; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P10; - /* ===== */ - #if (FXAA_QUALITY_PS > 11) - if(doneNP) { - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; - doneN = abs(lumaEndN) >= gradientScaled; - doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P11; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P11; - doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P11; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P11; - /* ===== */ - #if (FXAA_QUALITY_PS > 12) - if(doneNP) { - if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); - if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); - if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; - if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; - doneN = abs(lumaEndN) >= gradientScaled; - doneP = abs(lumaEndP) >= gradientScaled; - if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P12; - if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P12; - doneNP = (!doneN) || (!doneP); - if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P12; - if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P12; - /* ===== */ - } - #endif - /* ===== */ - } - #endif - /* ===== */ - } - #endif - /* ===== */ - } - #endif - /* ===== */ - } - #endif - /* ===== */ - } - #endif - /* ===== */ - } - #endif - /* ===== */ - } - #endif - /* ===== */ - } - #endif - /* ===== */ - } - #endif - /* ===== */ - } - /* ===== */ - FxaaFloat dstN = posM.x - posN.x; - FxaaFloat dstP = posP.x - posM.x; - if(!horzSpan) dstN = posM.y - posN.y; - if(!horzSpan) dstP = posP.y - posM.y; - /* ===== */ - FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; - FxaaFloat spanLength = (dstP + dstN); - FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; - FxaaFloat spanLengthRcp = 1.0/spanLength; - /* ===== */ - FxaaBool directionN = dstN < dstP; - FxaaFloat dst = min(dstN, dstP); - FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; - FxaaFloat subpixG = subpixF * subpixF; - FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; - FxaaFloat subpixH = subpixG * fxaaQualitySubpix; - /* ===== */ - FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; - FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); - if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; - if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; - #if (FXAA_DISCARD == 1) - return FxaaTexTop(tex, posM); - #else - return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); - #endif - } - /* ========== */ - #endif - void main() { - gl_FragColor = FxaaPixelShader( - vUv, - vec4(0.0), - tDiffuse, - tDiffuse, - tDiffuse, - resolution, - vec4(0.0), - vec4(0.0), - vec4(0.0), - 0.75, - 0.166, - 0.0833, - 0.0, - 0.0, - 0.0, - vec4(0.0) - ); - gl_FragColor.a = texture2D(tDiffuse, vUv).a; - }`; + fragmentShader = fragment; } diff --git a/src/three/shader/FractalMirrorShader.ts b/src/three/shader/FractalMirrorShader.ts index 8109550..ec63716 100644 --- a/src/three/shader/FractalMirrorShader.ts +++ b/src/three/shader/FractalMirrorShader.ts @@ -12,6 +12,9 @@ import {Vector2} from 'three'; import {BaseShader} from './BaseShader'; +import vertex from './vertex/Basic.glsl'; +import fragment from './fragment/FractalMirror.glsl'; + /** * Customized Kaleidoscope shader * Inspired by ackleyrc: https://www.shadertoy.com/view/llXcRl @@ -31,42 +34,7 @@ export class FractalMirrorShader implements BaseShader { invert: {value: false}, }; - vertexShader = ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - } - `; + vertexShader = vertex; - fragmentShader = ` - uniform sampler2D tDiffuse; - uniform vec2 iResolution; - uniform float numSides; - uniform bool invert; - - varying vec2 vUv; - - const float PI = 3.14159265358979323846; - - void main() { - vec2 center = vec2(0.5, 0.5); - float zoom = iResolution.x / iResolution.y; - vec2 uv = center - vUv; - if(zoom > 1.0) uv.y /= zoom; - else uv.x *= zoom; - - float KA = PI / numSides; - float angle = abs(mod(atan(uv.y, uv.x), 2.0 * KA) - KA); - if(zoom > 1.0) angle -= 45.0; - vec2 transformed = length(uv) * vec2(sin(angle), cos(angle)); - if(!invert) transformed += center; - else { - if(transformed.x < 0.0) transformed.x += 1.0; - if(transformed.y < 0.0) transformed.y += 1.0; - } - gl_FragColor = texture2D(tDiffuse, transformed); - } - `; + fragmentShader = fragment; } diff --git a/src/three/shader/LUTShader.ts b/src/three/shader/LUTShader.ts index 228aa0d..95fe15f 100644 --- a/src/three/shader/LUTShader.ts +++ b/src/three/shader/LUTShader.ts @@ -9,6 +9,9 @@ import {BaseShader} from './BaseShader'; +import vertex from './vertex/Basic.glsl'; +import fragment from './fragment/LUT.glsl'; + /** * LookUpTable shader * taken from ThreeJS examples and converted to TS @@ -27,56 +30,7 @@ export class LUTShader implements BaseShader { lutMapSize: {value: 1}, }; - vertexShader = ` - varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - } - `; + vertexShader = vertex; - fragmentShader = ` - #include - - #define FILTER_LUT true - - uniform sampler2D tDiffuse; - uniform sampler2D lutMap; - uniform float lutMapSize; - - varying vec2 vUv; - - vec4 sampleAs3DTexture(sampler2D tex, vec3 texCoord, float size) { - float sliceSize = 1.0 / size; // space of 1 slice - float slicePixelSize = sliceSize / size; // space of 1 pixel - float width = size - 1.0; - float sliceInnerSize = slicePixelSize * width; // space of size pixels - float zSlice0 = floor( texCoord.z * width); - float zSlice1 = min( zSlice0 + 1.0, width); - float xOffset = slicePixelSize * 0.5 + texCoord.x * sliceInnerSize; - float yRange = (texCoord.y * width + 0.5) / size; - float s0 = xOffset + (zSlice0 * sliceSize); - - #ifdef FILTER_LUT - - float s1 = xOffset + (zSlice1 * sliceSize); - vec4 slice0Color = texture2D(tex, vec2(s0, yRange)); - vec4 slice1Color = texture2D(tex, vec2(s1, yRange)); - float zOffset = mod(texCoord.z * width, 1.0); - return mix(slice0Color, slice1Color, zOffset); - - #else - - return texture2D(tex, vec2( s0, yRange)); - - #endif - } - - void main() { - vec4 originalColor = texture2D(tDiffuse, vUv); - vec4 tempColor = sampleAs3DTexture(lutMap, originalColor.xyz, lutMapSize); - tempColor.a = originalColor.a; - gl_FragColor = tempColor; - } - `; + fragmentShader = fragment; } diff --git a/src/three/shader/LUTShaderNearest.ts b/src/three/shader/LUTShaderNearest.ts index 701e89a..18db147 100644 --- a/src/three/shader/LUTShaderNearest.ts +++ b/src/three/shader/LUTShaderNearest.ts @@ -18,5 +18,5 @@ import {LUTShader} from './LUTShader'; export class LUTShaderNearest extends LUTShader { shaderID = 'LUTShaderNearest'; - fragmentShader = new LUTShader().fragmentShader.replace('#define FILTER_LUT', '//'); + fragmentShader = new LUTShader().fragmentShader.replace('#define FILTER_LUT true', ''); } diff --git a/src/three/shader/LuminosityHighPassShader.ts b/src/three/shader/LuminosityHighPassShader.ts index 1eeb1da..6b06413 100644 --- a/src/three/shader/LuminosityHighPassShader.ts +++ b/src/three/shader/LuminosityHighPassShader.ts @@ -11,6 +11,9 @@ import {Color} from 'three'; import {BaseShader} from './BaseShader'; +import vertex from './vertex/Basic.glsl'; +import fragment from './fragment/Luminosity.glsl'; + /** * Luminosity * http://en.wikipedia.org/wiki/Luminosity @@ -31,32 +34,7 @@ export class LuminosityHighPassShader implements BaseShader { defaultOpacity: {value: 0.0}, }; - vertexShader = ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - } - `; + vertexShader = vertex; - fragmentShader = ` - uniform sampler2D tDiffuse; - uniform vec3 defaultColor; - uniform float defaultOpacity; - uniform float luminosityThreshold; - uniform float smoothWidth; - - varying vec2 vUv; - - void main() { - - vec4 texel = texture2D( tDiffuse, vUv ); - vec3 luma = vec3( 0.299, 0.587, 0.114 ); - float v = dot( texel.xyz, luma ); - vec4 outputColor = vec4( defaultColor.rgb, defaultOpacity ); - float alpha = smoothstep( luminosityThreshold, luminosityThreshold + smoothWidth, v ); - gl_FragColor = mix( outputColor, texel, alpha ); - } - `; + fragmentShader = fragment; }; From 6f9e893c82d48655fbf17b8e8626fa2f864e24bf Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Mon, 18 Oct 2021 12:55:01 +0200 Subject: [PATCH 38/76] move audioprocessing to its own folder --- src/weas/Bea.ts | 904 +++++++++++++++++++++++++++++++++++++++++ src/weas/WEAS.ts | 158 ++++--- src/weas/WEAS.wasm.asc | 107 +++-- src/weas/index.ts | 12 + 4 files changed, 1102 insertions(+), 79 deletions(-) create mode 100644 src/weas/Bea.ts create mode 100644 src/weas/index.ts diff --git a/src/weas/Bea.ts b/src/weas/Bea.ts new file mode 100644 index 0000000..4dc241a --- /dev/null +++ b/src/weas/Bea.ts @@ -0,0 +1,904 @@ +/* eslint-disable guard-for-in */ + +/* +* BeatDetektor.js +* +* BeatDetektor - CubicFX Visualizer Beat Detection & Analysis Algorithm +* Javascript port by Charles J. Cliffe and Corban Brook +* +* Copyright (c) 2009 Charles J. Cliffe. +* +* BeatDetektor is distributed under the terms of the MIT License. +* http://opensource.org/licenses/MIT +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + +/** +* @public +*/ +export class Bea_ts { + bd_low: BeatsDetektor = new BeatsDetektor(55, 109); + bd_med: BeatsDetektor = new BeatsDetektor(85, 169); + bd_high: BeatsDetektor = new BeatsDetektor(100, 199); + bd_ext: BeatsDetektor = new BeatsDetektor(150, 299); + + winRewards: number[] = []; + + bds: BeatsDetektor[] = []; + bdi = 0; + + prev_bpm: number = -1; + prev_win: number = -1; + + /** + * Construct BeatDetektor array + * @param {boolean} autoReset stfu + */ + constructor(autoReset = true) { + this.bds.push(this.bd_low, this.bd_med, this.bd_high, this.bd_ext); + + if (autoReset) setInterval(() => this.resetBds(), 20 * 1000); + } + + /** + * stfu eslint + */ + private resetBds(): void { + let i = this.bdi++; + const l = this.bds.length; + if (i >= l) this.bdi = i = i % l; + this.bds[i].reset(); + } + + /** + * Bpm-Process current data + * @param {number} time timestamp as float + * @param {Array} data fftData + * @return {Array} + * @public + */ + public process(time, data) { + const groupings = true; + const chooseAvg = false; + + let results = []; + + // weigh previous result in + if (this.prev_bpm > 0) { + results.push({value: this.prev_bpm, weight: this.prev_win}); + } + + this.bds.forEach((bd) => { + bd.process(time, data) / 10 || 1; + if (bd.bpm_contest.length > 0) { + const tmp = []; + let maxW = 0; + bd.bpm_contest.forEach((val, key) => { + tmp.push({value: key / 10, weight: val}); + if (val > maxW) maxW = val; + }); + const srt = tmp.sort((a, b) => b.weight- a.weight); + for (let i = 0; i < Math.min(srt.length, 10); i++) { + const obj = srt[i]; + results.push({value: obj.value, weight: obj.weight /* / maxW */}); + } + } + }); + + results = results.sort((a, b) => b.weight - a.weight).slice(0, 15); + + let candidates = []; + + if (results.length > 0) { + for (let r_i = 0; r_i < results.length; r_i++) { + // calc sum + let resWeight = 0; + // get result to multiply + const result = results[r_i]; + for (let i_i = 0; i_i < results.length; i_i++) { + // skip own multiplicator + if (r_i == i_i) continue; + // get object to compare + const comp = results[i_i]; + // determine min/max multiply relation + const mx = Math.max(result.value, comp.value); + const mn = Math.min(result.value, comp.value); + const ismn = comp.value == mn; + let mult = mx / mn; + if (mult > 2.5 && mult < 3.5) mult /= 3; // max is ~ 3x multiple of min + else if (mult > 1.5 && mult < 2.50) mult /= 2; // max is ~ 2x multiple of min + // do weighing calculation + let weight = 0; + if (mult > 0.96 && mult < 1.04) weight = (ismn ? 1.0 : 1.0) - Math.abs(mult - 1.00); // is 1/1 beat + if (mult > 0.64 && mult < 0.68) weight = (ismn ? 0.8 : 0.5) - Math.abs(mult - 0.66); // is 2/3 beat + if (mult > 0.72 && mult < 0.78) weight = (ismn ? 0.3 : 0.4) - Math.abs(mult - 0.75); // is 3/4 beat + // calculate sum + resWeight += weight * comp.weight; + } + // push matrix multiplied result average + let value = result.value; + if (value < 90) value *= 2; + if (value > 180) value /= 2; + candidates.push({value, weight: (result.weight * 2 + resWeight) / 3}); + } + + // group by rounded numbers x,0 | x,5 | x+1,0 + if (groupings) { + const grps = []; + candidates.forEach((c, i) => { + // xxx.0 bpm weights + let val = Math.round(c.value * 10); + const rst = val % 10; + if (rst < 3) val -= rst; + else if (rst > 7) val += 10 - rst; + else val += 5 - rst; + if (!grps[val]) grps[val] = 0; + grps[val] += c.weight; + // overall winner Bpm weights + const v2 = Math.round(c.value); + if (!isNaN(this.winRewards[v2]) && this.winRewards[v2] > 1) { + grps[val] = (grps[val] + this.winRewards[v2]) / 2; + } + }); + candidates = grps.map((v, i) =>{ + return {value: i / 10, weight: v}; + }); + } + + // sort and get Top-10 matches + candidates = candidates.sort((a, b) => b.weight - a.weight).slice(0, 10); + + // get average + if (chooseAvg) { + const half = (candidates.length / 2); + let avg = 0; + candidates.forEach((c, i) => avg += i < half ? c.value : 0); + avg /= half; + // choose closest to average + let choiceI = -1; + let choiceV = 0; + for (let index = 0; index < candidates.length; index++) { + const cand = candidates[index]; + const diffC = Math.abs(choiceV - cand.value); + const diffA = Math.abs(avg - cand.value); + if (choiceI < 0 || diffA < diffC) { + choiceI = index; + choiceV = cand.value; + } + } + + if (choiceI != 0) { + const can0 = candidates.splice(choiceI, 1); + candidates.unshift(can0); + } + } + + if (Math.random() < 0.01) console.log(results, candidates); + } + + /* + const low = this.bd_low.process(time, data) / 10.0; + const med = this.bd_med.process(time, data) / 10.0; + const high = this.bd_high.process(time, data) / 10.0; + + if (low != 1 || med != 1 || high != 1) { + let dekt: BeatsDetektor; + + if (pre == low) { + dekt = this.bd_low; + } else if (pre == med) { + dekt = this.bd_med; + } else if (pre == high) { + dekt = this.bd_high; + } else { + // multiplier matching + const ml = med / low; + const hm = high / med; + const hl = high / low; + // average matching + const is_ml = (ml > 1.9 && ml < 2.1); + const is_hm = (hm > 1.9 && hm < 2.1); + const is_hl = (hl > 1.9 && hl < 2.1) || (hl > 2.7 && hl < 3.3); + // console.log('Matching results: ', is_ml, is_hm, is_hl); + + const bts_l = this.bd_low.win_val; + const bts_m = this.bd_med.win_val; + const bts_h = this.bd_high.win_val; + // matching bpm? + if (is_hl) { + dekt = this.bd_low; + } else if (is_hm || is_ml) { + dekt = this.bd_med; + } else { + const mx = Math.max(bts_l, bts_m, bts_h); + dekt = (mx == bts_l) ? this.bd_low : (mx == bts_m) ? this.bd_med : this.bd_high; + } + + // this.vu.process(this.bd_med); + // const kick = this.kick_det.process(this.bd_med); + } + + // take most probable bpm + this.prev_bpm = dekt.win_bpm_int / 10.0; + } + */ + + // process rewards + this.winRewards.forEach((r, i) => { + this.winRewards[i] = r > 1 ? r * 0.6 : r; + }); + if (candidates.length > 0) { + const o = candidates[0]; + const ovf = Math.floor(o.value); + if (!this.winRewards[ovf]) this.winRewards[ovf] = 0; + this.winRewards[ovf] = (this.winRewards[ovf] + o.weight) / 2; + } + + return candidates.slice(0, 3); + } +} + + +/** +BeatDetektor class + +Theory: + +Trigger detection is performed using a trail of moving averages, +The FFT input is broken up into 128 ranges and averaged, each range has two moving +averages that tail each other at a rate of (1.0 / BD_DETECTION_RATE) seconds. + +Each time the moving average for a range exceeds it's own tailing average by: + +(moving_average[range] * BD_DETECTION_FACTOR >= moving_average[range]) + +if this is true there's a rising edge and a detection is flagged for that range. +Next a trigger gap test is performed between rising edges and timestamp recorded. + +If the gap is larger than our BPM window (in seconds) then we can discard it and +reset the timestamp for a new detection -- but only after checking to see if it's a +reasonable match for 2* the current detection in case it's only triggered every +other beat. Gaps that are lower than the BPM window are ignored and the last +timestamp will not be reset. + +Gaps that are within a reasonable window are run through a quality stage to determine +how 'close' they are to that channel's current prediction and are incremented or +decremented by a weighted value depending on accuracy. Repeated hits of low accuracy +will still move a value towards erroneous detection but it's quality will be lowered +and will not be eligible for the gap time quality draft. + +Once quality has been assigned ranges are reviewed for good match candidates and if +BD_MINIMUM_CONTRIBUTIONS or more ranges achieve a decent ratio (with a factor of BD_QUALITY_TOLERANCE) +of contribution to the overall quality we take them into the +contest round. Note that the contest round won't run on a given process() call if +the total quality achieved does not meet or exceed BD_QUALITY_TOLERANCE. +Each time through if a select draft of BPM ranges has achieved a reasonable quality +above others it's awarded a value in the BPM contest. The BPM contest is a hash +array indexed by an integer BPM value, each draft winner is awarded BD_QUALITY_REWARD. +Finally the BPM contest is examined to determine a leader and all contest entries +are normalized to a total value of BD_FINISH_LINE, whichever range is closest to +BD_FINISH_LINE at any given point is considered to be the best guess however waiting +until a minimum contest winning value of about 20.0-25.0 will provide more accurate +results. Note that the 20-25 rule may vary with lower and higher input ranges. +A winning value that exceeds 40 or hovers around 60 (the finish line) is pretty much +a guaranteed match. +Configuration Kernel Notes: +The majority of the ratios and values have been reverse-engineered from my own +observation and visualization of information from various aspects of the detection +triggers; so not all parameters have a perfect definition nor perhaps the best value yet. +However despite this it performs very well; I had expected several more layers +before a reasonable detection would be achieved. Comments for these parameters will be +updated as analysis of their direct effect is explored. +Input Restrictions: +bpm_maximum must be within the range of (bpm_minimum*2)-1 +i.e. minimum of 50 must have a maximum of 99 because 50*2 = 100 +Changelog: +01/17/2010 - Charles J. Cliffe +- Tested and tweaked default kernel values for tighter detection +- Added BeatDetektor.config_48_95, BeatDetektor.config_90_179 and BeatDetektor.config_150_280 for more refined detection ranges +- Updated unit test to include new range config example +02/21/2010 - Charles J. Cliffe +- Fixed numerous bugs and divide by 0 on 1% match causing poor accuracy +- Re-worked the quality calulations, accuracy improved 8-10x +- Primary value is now a fractional reading (*10, just divide by 10), added win_bpm_int_lo for integral readings +- Added feedback loop for current_bpm to help back-up low quality channels +- Unified range configs, now single default should be fine +- Extended quality reward 'funnel' +10/07/2021 - hexxone +- Convert to TypeScript + +* @public +*/ +class BeatsDetektor { + config = { + BD_DETECTION_RANGES: 128, // How many ranges to quantize the FFT into + BD_DETECTION_RATE: 12.0, // Rate in 1.0 / BD_DETECTION_RATE seconds + BD_DETECTION_FACTOR: 0.915, // Trigger ratio + BD_QUALITY_DECAY: 0.5, // range and contest decay + BD_QUALITY_TOLERANCE: 0.96, // Use the top x % of contest results + BD_QUALITY_REWARD: 10.0, // Award weight + BD_QUALITY_STEP: 0.1, // Award step (roaming speed) + BD_MINIMUM_CONTRIBUTIONS: 8, // At least x ranges must agree to process a result + BD_FINISH_LINE: 60.0, // Contest values wil be normalized to this finish line + // this is the 'funnel' that pulls ranges in / out of alignment based on trigger detection + BD_REWARD_TOLERANCES: [0.001, 0.005, 0.01, 0.02, 0.04, 0.08, 0.10, 0.15, 0.30], // .1%, .5%, 1%, 2%, 4%, 8%, 10%, 15% + BD_REWARD_MULTIPLIERS: [20.0, 10.0, 8.0, 1.0, 1.0/2.0, 1.0/4.0, 1.0/8.0, 1/16.0, 1/32.0], + }; + + BPM_MIN: any; + BPM_MAX: any; + + beat_counter: number = 0; + half_counter: number = 0; + quarter_counter: number = 0; + + a_freq_range: Float64Array; + ma_freq_range: Float64Array; + maa_freq_range: Float64Array; + + last_detection: Float64Array; + + ma_bpm_range: Float64Array; + maa_bpm_range: Float64Array; + + detection_quality: Float64Array; + detection: Array; + + ma_quality_avg: number = 0; + ma_quality_total: number = 0; + + bpm_contest: number[]; + bpm_contest_lo: number[]; + + quality_total: number = 0; + quality_avg: number = 0; + + current_bpm: number = 0; + current_bpm_lo: number = 0; + winning_bpm: number = 0; + win_val: number = 0; + winning_bpm_lo: number = 0; + win_val_lo: number = 0; + + win_bpm_int: number = 0; + win_bpm_int_lo: number = 0; + + bpm_predict: number = 0; + is_erratic: boolean = false; + + bpm_offset: number = 0; + + last_timer: number = 0; + last_update: number = 0; + bpm_timer: number = 0; + maa_quality_avg: number = 0; + + /** + * Create new instance of BeatsDetektor. See above for details + * @param {number} bpm_minimum estimation lower bound + * @param {number} bpm_maximum estimation upper bound + * @param {Object} alt_config alternative config (optional) + * @public + */ + constructor(bpm_minimum = 85, bpm_maximum = 169, alt_config?) { + if (alt_config) this.config = alt_config; + + this.BPM_MIN = bpm_minimum; + this.BPM_MAX = bpm_maximum; + + // current average (this sample) for range n + this.a_freq_range = new Float64Array(this.config.BD_DETECTION_RANGES); + // moving average of frequency range n + this.ma_freq_range = new Float64Array(this.config.BD_DETECTION_RANGES); + // moving average of moving average of frequency range n + this.maa_freq_range = new Float64Array(this.config.BD_DETECTION_RANGES); + // timestamp of last detection for frequecy range n + this.last_detection = new Float64Array(this.config.BD_DETECTION_RANGES); + + // moving average of gap lengths + this.ma_bpm_range = new Float64Array(this.config.BD_DETECTION_RANGES); + // moving average of moving average of gap lengths + this.maa_bpm_range = new Float64Array(this.config.BD_DETECTION_RANGES); + + // range n quality attribute, good match = quality+, bad match = quality-, min = 0 + this.detection_quality = new Float64Array(this.config.BD_DETECTION_RANGES); + + // current trigger state for range n + this.detection = new Array(this.config.BD_DETECTION_RANGES); + + this.reset(); + + if (console) { + console.log('BeatsDetektor('+this.BPM_MIN+','+this.BPM_MAX+') created.'); + } + } + + /** + * Null everthing + * @public + */ + public reset() { + // var bpm_avg = 60.0/((this.BPM_MIN+this.BPM_MAX)/2.0); + + for (let i = 0; i < this.config.BD_DETECTION_RANGES; i++) { + this.a_freq_range[i] = 0.0; + this.ma_freq_range[i] = 0.0; + this.maa_freq_range[i] = 0.0; + this.last_detection[i] = 0.0; + + this.ma_bpm_range[i] = + this.maa_bpm_range[i] = 60.0/this.BPM_MIN + ((60.0/this.BPM_MAX-60.0/this.BPM_MIN) * (i/this.config.BD_DETECTION_RANGES)); + + this.detection_quality[i] = 0.0; + this.detection[i] = false; + } + + this.ma_quality_avg = 0; + this.ma_quality_total = 0; + + this.bpm_contest = []; + this.bpm_contest_lo = []; + + this.quality_total = 0.0; + this.quality_avg = 0.0; + + this.current_bpm = 0.0; + this.current_bpm_lo = 0.0; + + this.winning_bpm = 0.0; + this.win_val = 0.0; + this.winning_bpm_lo = 0.0; + this.win_val_lo = 0.0; + + this.win_bpm_int = 0; + this.win_bpm_int_lo = 0; + + this.bpm_predict = 0; + + this.is_erratic = false; + this.bpm_offset = 0.0; + this.last_timer = 0.0; + this.last_update = 0.0; + + this.bpm_timer = 0.0; + this.beat_counter = 0; + this.half_counter = 0; + this.quarter_counter = 0; + } + + /** + * Process BPM for fft Data Frame + * @param {number} timer_seconds + * @param {Array} fft_data + * @return {number} win_bpm_int + * @public + */ + public process(timer_seconds, fft_data): number { + // first call + if (!this.last_timer) { + this.last_timer = timer_seconds; + return; + } + // ignore 0 start time + if (this.last_timer > timer_seconds) { + this.reset(); + return; + } + + const timestamp = timer_seconds; + + this.last_update = timer_seconds - this.last_timer; + this.last_timer = timer_seconds; + + // reset after 3 second silence + if (this.last_update > 3.0) { + this.reset(); + return; + } + + let i; + let x; + let v; + + const bpm_floor = 60.0/this.BPM_MAX; + const bpm_ceil = 60.0/this.BPM_MIN; + + const range_step = (fft_data.length / this.config.BD_DETECTION_RANGES); + let range = 0; + + + for (x=0; x= this.maa_freq_range[range]); + + // compute bpm clamps for comparison to gap lengths + + // clamp detection averages to input ranges + if (this.ma_bpm_range[range] > bpm_ceil) this.ma_bpm_range[range] = bpm_ceil; + if (this.ma_bpm_range[range] < bpm_floor) this.ma_bpm_range[range] = bpm_floor; + if (this.maa_bpm_range[range] > bpm_ceil) this.maa_bpm_range[range] = bpm_ceil; + if (this.maa_bpm_range[range] < bpm_floor) this.maa_bpm_range[range] = bpm_floor; + + let rewarded = false; + + // new detection since last, test it's quality + if (!this.detection[range] && det) { + // calculate length of gap (since start of last trigger) + let trigger_gap = timestamp-this.last_detection[range]; + + // trigger falls within acceptable range, + if (trigger_gap < bpm_ceil && trigger_gap > (bpm_floor)) { + // compute gap and award quality + + // use our tolerances as a funnel to edge detection towards the most likely value + for (i = 0; i < this.config.BD_REWARD_TOLERANCES.length; i++) { + if (Math.abs(this.ma_bpm_range[range]-trigger_gap) < this.ma_bpm_range[range]*this.config.BD_REWARD_TOLERANCES[i]) { + this.detection_quality[range] += this.config.BD_QUALITY_REWARD * this.config.BD_REWARD_MULTIPLIERS[i]; + rewarded = true; + } + } + + if (rewarded) { + this.last_detection[range] = timestamp; + } + } else if (trigger_gap >= bpm_ceil) { + // low quality, gap exceeds maximum time + // start a new gap test, next gap is guaranteed to be longer + + // test for 1/2 beat + trigger_gap /= 2.0; + + if (trigger_gap < bpm_ceil && trigger_gap > (bpm_floor)) { + for (i = 0; i < this.config.BD_REWARD_TOLERANCES.length; i++) { + if (Math.abs(this.ma_bpm_range[range]-trigger_gap) < this.ma_bpm_range[range]*this.config.BD_REWARD_TOLERANCES[i]) { + this.detection_quality[range] += this.config.BD_QUALITY_REWARD * this.config.BD_REWARD_MULTIPLIERS[i]; + rewarded = true; + } + } + } + + + // decrement quality if no 1/2 beat reward + if (!rewarded) { + trigger_gap *= 2.0; + } + this.last_detection[range] = timestamp; + } + + if (rewarded) { + let qmp = (this.detection_quality[range]/this.quality_avg)*this.config.BD_QUALITY_STEP; + if (qmp > 1.0) { + qmp = 1.0; + } + + this.ma_bpm_range[range] -= (this.ma_bpm_range[range]-trigger_gap) * qmp; + this.maa_bpm_range[range] -= (this.maa_bpm_range[range]-this.ma_bpm_range[range]) * qmp; + } else if (trigger_gap >= bpm_floor && trigger_gap <= bpm_ceil) { + if (this.detection_quality[range] < this.quality_avg*this.config.BD_QUALITY_TOLERANCE && this.current_bpm) { + this.ma_bpm_range[range] -= (this.ma_bpm_range[range]-trigger_gap) * this.config.BD_QUALITY_STEP; + this.maa_bpm_range[range] -= (this.maa_bpm_range[range]-this.ma_bpm_range[range]) * this.config.BD_QUALITY_STEP; + } + this.detection_quality[range] -= this.config.BD_QUALITY_STEP; + } else if (trigger_gap >= bpm_ceil) { + if ((this.detection_quality[range] < this.quality_avg*this.config.BD_QUALITY_TOLERANCE) && this.current_bpm) { + this.ma_bpm_range[range] -= (this.ma_bpm_range[range]-this.current_bpm) * 0.5; + this.maa_bpm_range[range] -= (this.maa_bpm_range[range]-this.ma_bpm_range[range]) * 0.5; + } + this.detection_quality[range]-= this.config.BD_QUALITY_STEP; + } + } + + if ((!rewarded && timestamp-this.last_detection[range] > bpm_ceil) || (det && Math.abs(this.ma_bpm_range[range]-this.current_bpm) > this.bpm_offset)) { + this.detection_quality[range] -= this.detection_quality[range]*this.config.BD_QUALITY_STEP*this.config.BD_QUALITY_DECAY*this.last_update; + } + + // quality bottomed out, set to 0 + if (this.detection_quality[range] < 0.001) this.detection_quality[range]=0.001; + + this.detection[range] = det; + + range++; + } + + // total contribution weight + this.quality_total = 0; + + // total of bpm values + let bpm_total = 0; + // number of bpm ranges that contributed to this test + let bpm_contributions = 0; + + + // accumulate quality weight total + for (let x=0; x= this.ma_quality_avg) { + if (this.ma_bpm_range[x] < bpm_ceil && this.ma_bpm_range[x] > bpm_floor) { + // eslint-disable-next-line no-unused-vars + bpm_total += this.maa_bpm_range[x]; + + let draft_float = Math.round((60.0/this.maa_bpm_range[x])*1000.0); + + draft_float = (Math.abs(Math.ceil(draft_float)-(60.0/this.current_bpm)*1000.0)<(Math.abs(Math.floor(draft_float)-(60.0/this.current_bpm)*1000.0)))?Math.ceil(draft_float/10.0):Math.floor(draft_float/10.0); + const draft_int = parseInt(draft_float/10.0 as any); + // if (draft_int) console.log(draft_int); + if (isNaN(draft[draft_int])) draft[draft_int] = 0; + + draft[draft_int]+=this.detection_quality[x]/this.quality_avg; + bpm_contributions++; + if (offset_test_bpm == 0.0) offset_test_bpm = this.maa_bpm_range[x]; + else { + avg_bpm_offset += Math.abs(offset_test_bpm-this.maa_bpm_range[x]); + } + } + } + } + } + + // if we have one or more contributions that pass criteria then attempt to display a guess + const has_prediction = bpm_contributions>=this.config.BD_MINIMUM_CONTRIBUTIONS; + + let draft_winner=0; + let win_val = 0; + + if (has_prediction) { + for (const draft_i in draft) { + if (draft[draft_i] > win_val) { + win_val = draft[draft_i]; + draft_winner = parseInt(draft_i); + } + } + + this.bpm_predict = 60.0/(draft_winner/10.0); + + avg_bpm_offset /= bpm_contributions; + this.bpm_offset = avg_bpm_offset; + + if (!this.current_bpm) { + this.current_bpm = this.bpm_predict; + } + } + + if (this.current_bpm && this.bpm_predict) this.current_bpm -= (this.current_bpm-this.bpm_predict)*this.last_update; + + // hold a contest for bpm to find the current mode + let contest_max=0; + + for (const contest_i in this.bpm_contest) { + if (contest_max < this.bpm_contest[contest_i]) contest_max = this.bpm_contest[contest_i]; + if (this.bpm_contest[contest_i] > this.config.BD_FINISH_LINE/2.0) { + const draft_int_lo = parseInt(Math.round((contest_i as any)/10.0) as any); + if (isNaN(this.bpm_contest_lo[draft_int_lo])) this.bpm_contest_lo[draft_int_lo] = 0; + this.bpm_contest_lo[draft_int_lo]+= (this.bpm_contest[contest_i]/6.0)*this.last_update; + } + } + + // normalize to a finish line + if (contest_max > this.config.BD_FINISH_LINE) { + for (const contest_i in this.bpm_contest) { + this.bpm_contest[contest_i]=(this.bpm_contest[contest_i]/contest_max)*this.config.BD_FINISH_LINE; + } + } + + contest_max = 0; + for (const contest_i in this.bpm_contest_lo) { + if (contest_max < this.bpm_contest_lo[contest_i]) contest_max = this.bpm_contest_lo[contest_i]; + } + + // normalize to a finish line + if (contest_max > this.config.BD_FINISH_LINE) { + for (const contest_i in this.bpm_contest_lo) { + this.bpm_contest_lo[contest_i]=(this.bpm_contest_lo[contest_i]/contest_max)*this.config.BD_FINISH_LINE; + } + } + + + // decay contest values from last loop + for (const contest_i in this.bpm_contest) { + this.bpm_contest[contest_i]-=this.bpm_contest[contest_i]*(this.last_update/this.config.BD_DETECTION_RATE); + } + + // decay contest values from last loop + for (const contest_i in this.bpm_contest_lo) { + this.bpm_contest_lo[contest_i]-=this.bpm_contest_lo[contest_i]*(this.last_update/this.config.BD_DETECTION_RATE); + } + + this.bpm_timer+=this.last_update; + + let winner; + let winner_lo; + + // attempt to display the beat at the beat interval ;) + if (this.bpm_timer > this.winning_bpm/4.0 && this.current_bpm) { + this.win_val = 0; + this.win_val_lo = 0; + + if (this.winning_bpm) while (this.bpm_timer > this.winning_bpm/4.0) this.bpm_timer -= this.winning_bpm/4.0; + + // increment beat counter + this.quarter_counter++; + this.half_counter= Math.floor(this.quarter_counter/2); + this.beat_counter = Math.floor(this.quarter_counter/4); + + // award the winner of this iteration + const idx = Math.floor(Math.round((60.0/this.current_bpm)*10.0)); + if (isNaN(this.bpm_contest[idx])) this.bpm_contest[idx] = 0; + this.bpm_contest[idx]+=this.config.BD_QUALITY_REWARD; + + // find the overall winner so far + for (const contest_i in this.bpm_contest) { + if (this.win_val < this.bpm_contest[contest_i]) { + winner = contest_i; + this.win_val = this.bpm_contest[contest_i]; + } + } + + if (winner) { + this.win_bpm_int = parseInt(winner); + this.winning_bpm = (60.0/(winner/10.0)); + } + + // find the overall winner so far + for (const contest_i in this.bpm_contest_lo) { + if (this.win_val_lo < this.bpm_contest_lo[contest_i]) { + winner_lo = contest_i; + this.win_val_lo = this.bpm_contest_lo[contest_i]; + } + } + + if (winner_lo) { + this.win_bpm_int_lo = parseInt(winner_lo); + this.winning_bpm_lo = 60.0/winner_lo; + } + + if (console && (this.beat_counter % 4) == 0) { + // console.log(`Bea.ts(${this.BPM_MIN}, ${this.BPM_MAX}): [ Current Estimate: ${winner} BPM ] [ Time: ${Math.floor(timer_seconds*1000.0)/1000.0}s, Quality: ${Math.floor(this.quality_total*1000.0)/1000.0}, Rank: ${Math.floor(this.win_val*1000.0)/1000.0}, Jitter: ${Math.floor(this.bpm_offset*1000000.0)/1000000.0} ]`); + } + } + + return this.win_bpm_int; + } +} + +/** +* simple bass kick visualizer assistant module +* @public +*/ +// class BeatsKick { +// is_kick = false; + +// /** +// * Post-Process current Detektor frame and check for kick +// * @param {BeatsDetektor} det +// * @return {boolean} +// */ +// process(det: BeatsDetektor) { +// return this.is_kick = ((det.detection[0] && det.detection[1]) || (det.ma_freq_range[0]/det.maa_freq_range[0])>1.4); +// } + +// /** +// * @return {boolean} last kick status +// */ +// isKick() { +// return this.is_kick; +// } +// } + + +/** +* simple vu spectrum visualizer assistant module +*/ +// class BeatSpect { +// vu_levels = []; + +// /** +// * Post-Process current Detektor frame +// * @param {BeatsDetektor} det +// * @param {number} lus last update time (optional) +// */ +// process(det: BeatsDetektor, lus?) { +// let i; let det_val; let det_max = 0.0; +// if (isNaN(lus)) lus = det.last_update; + +// for (i = 0; i < det.config.BD_DETECTION_RANGES; i++) { +// det_val = (det.ma_freq_range[i]/det.maa_freq_range[i]); +// if (det_val > det_max) det_max = det_val; +// } + +// if (det_max <= 0) det_max = 1.0; + +// for (i = 0; i < det.config.BD_DETECTION_RANGES; i++) { +// det_val = (det.ma_freq_range[i]/det.maa_freq_range[i]); // fabs(fftData[i*2]/2.0); + +// if (det_val != det_val) det_val = 0; + +// // && (det_val > this.vu_levels[i]) +// if (det_val>1.0) { +// det_val -= 1.0; +// if (det_val>1.0) det_val = 1.0; + +// if (det_val > this.vu_levels[i]) { +// this.vu_levels[i] = det_val; +// } else if (det.current_bpm) this.vu_levels[i] -= (this.vu_levels[i]-det_val)*lus*(1.0/det.current_bpm)*3.0; +// } else { +// if (det.current_bpm) this.vu_levels[i] -= (lus/det.current_bpm)*2.0; +// } + +// if (this.vu_levels[i] < 0 || this.vu_levels[i] != this.vu_levels[i]) this.vu_levels[i] = 0; +// } +// } + +// /** +// * returns vu level for BD_DETECTION_RANGES range[x] +// * @param {number} x +// * @return {number} +// */ +// getLevel(x) { +// return this.vu_levels[x]; +// } +// } + + diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 143ebf1..88a2df6 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -7,14 +7,16 @@ * See LICENSE file in the project root for full license information. */ -import {CComponent} from './CComponent'; -import {CSettings} from './CSettings'; -import {waitReady} from './Util'; -import {Smallog} from './Smallog'; +import {CComponent} from '../CComponent'; +import {CSettings} from '../CSettings'; +import {waitReady} from '../Util'; +import {Smallog} from '../Smallog'; -import {wascWorker, WascInterface} from './wasc-worker'; +import {wascWorker, WascInterface} from '../wasc-worker'; +import {Bea_ts} from './Bea'; const DAT_LEN = 128; +const LISTENAME = 'wallpaperRegisterAudioListener'; /** * Audio processing settings @@ -25,7 +27,7 @@ export class WEASettings extends CSettings { debugging: boolean = false; /** do audio processing? */ audioprocessing: boolean = true; - // do pink-noise processing? + // do dynamic processing? equalize: boolean = true; // convert to mono? mono_audio: boolean = true; @@ -39,9 +41,9 @@ export class WEASettings extends CSettings { audio_increase: number = 75; audio_decrease: number = 25; // multipliers - treble_multiplier: number = 0.5; - mids_multiplier: number = 0.75; - bass_multiplier: number = 1.8; + treble_multiplier: number = 0.666; + mids_multiplier: number = 0.8; + bass_multiplier: number = 1.2; // ignore value leveling for "silent" data minimum_volume: number = 0.005; // use low latency audio? @@ -68,6 +70,10 @@ export interface WEAudio { range: number; silent: number; intensity: number; + bpm?: [{ + value: number, + weight: number + }] } /** @@ -132,6 +138,9 @@ export class WEAS extends CComponent { // web assembly functions private weasModule: WascInterface = null; + // bpm detektor + private bpModule: Bea_ts; + // debug render elements private mainElm: HTMLDivElement; private canvas1: HTMLCanvasElement; @@ -142,12 +151,29 @@ export class WEAS extends CComponent { /** * delay audio initialization until page ready + * @param {boolean} detectBpm (optional) */ - constructor() { + constructor(detectBpm = false) { super(); + if (detectBpm) this.bpModule = new Bea_ts(); waitReady().then(() => this.realInit()); } + + /** + * convert float based audio to int + * @param {Array} data + * @return {Array} + */ + private convertAudio(data) { + const stereo = []; + const dl = data.length; + for (let i = 0; i < dl; i++) { + stereo[i] = Math.max(0, Math.floor(data[i] * 255)); + } + return stereo; + } + /** * initializes audio processing pipeline * and starts listening on audio data @@ -155,48 +181,51 @@ export class WEAS extends CComponent { */ private async realInit() { // only listen if wallpaper engine context given - if (!window['wallpaperRegisterAudioListener']) { - Smallog.info('\'window.wallpaperRegisterAudioListener\' not given!'); + if (!window[LISTENAME]) { + Smallog.error('\'window.wallpaperRegisterAudioListener\' not given!'); return; } this.injectCSS(); this.injectHTML(); - this.weasModule = await wascWorker('WEAS.wasm', 4096, {}, !this.settings.low_latency); + wascWorker('WEAS.wasm', 4096, {}, !this.settings.low_latency).then((mod) => { + this.weasModule = mod; - // assert - if (this.weasModule) Smallog.debug('Got Audio provider!'); - else { - Smallog.error('Could not create WebAssembly Audio provider! [Null-Object]'); - return; - } + if (mod.memoryBuffer) { + setInterval(() => { + console.log('Yeah Boiii, we got shared memory access to WebAssembly worker!'); + console.log(mod.memoryBuffer); + }, 10000); + } - // pass settings to module - await this.updateSettings(); + this.updateSettings().then(() => { + this.registerListener(); + Smallog.debug('WebAssembly Audio provider is ready!'); + }); + }).catch((err) => { + const m = `Could not create WebAssembly Audio provider! ErrMsg: ${err}`; + Smallog.error(m); + alert(m); + }); + } + /** + * Registers the wallpaper engine audio listener + * @ignore + */ + private registerListener() { const {run} = this.weasModule; const self = this; // register audio callback on module - window['wallpaperRegisterAudioListener']((audioArray) => { + window[LISTENAME]((audioArray) => { // Smallog.debug('Get Audio Data!'); // check basic if (!self.settings.audioprocessing || audioArray == null || audioArray.length != DAT_LEN) { - Smallog.error('audioListener: received invalid audio data array: ' + JSON.stringify([audioArray.length || null, audioArray])); + Smallog.error('received invalid audio data: ' + JSON.stringify([audioArray.length || null, audioArray])); return; } - // check nulls - let consecutiveNull = 0; - for (let i = 1; i < 18; i++) { - const aA = audioArray[i * 2]; - if (aA == 0.0) consecutiveNull++; - else consecutiveNull = 0; - if (consecutiveNull > 16) { - Smallog.debug('Skipping received Null data!: ' + JSON.stringify(audioArray)); - return; - } - } // prepare data const start = performance.now(); @@ -210,35 +239,45 @@ export class WEAS extends CComponent { // set audio data directly in module memory exports.__getFloat64ArrayView(ex.inputData).set(arrData); - // trigger processing processing + // trigger processing ex.update(); - // get copy of updated Data & Properties - const r = { // => result + // get copy of webassembly data + const r = { + // ptr: {data: ex.outputData, props: ex.audioProps}, data: new Float64Array(exports.__getFloat64ArrayView(ex.outputData)).buffer, props: new Float64Array(exports.__getFloat64ArrayView(ex.audioProps)).buffer, }; return r; - }, // params passed to worker + }, { data: self.inBuff.buffer, }) - // worker result, back in main context .then((result) => { + // worker result, back in main context const {data, props} = result; const arrData = new Float64Array(data); const arrProps = new Float64Array(props); const realProps = self.getProps(arrProps); const teim = performance.now() - start; + + if (self.lastAudio && !self.lastAudio.silent && realProps.silent) { + console.log(audioArray, arrData, arrProps); + } + + let bpmObj = {}; + if (this.bpModule && !realProps.silent) { + bpmObj = this.bpModule.process(start / 1000.0, this.convertAudio(audioArray)); + } + // apply actual last Data from worker self.lastAudio = { time: start, ellapsed: teim, data: arrData, ...realProps, + bpm: bpmObj, } as any; - // print info - // Smallog.debug('Got Data from Worker! Time= ' + teim + ', Data= ' + JSON.stringify(realProps)); }); }); } @@ -254,15 +293,19 @@ export class WEAS extends CComponent { opacity: 0; position: absolute; z-index: 2; - background: #000000aa; } #weas-preview canvas { - background: none; + background: #06060666; border: white 2px dotted; } #weas-preview.show { opacity: 1; } + #WEASDisplay { + position: fixed; + left: 0px; + width: 100vw; + } `; document.head.append(st); } @@ -303,7 +346,7 @@ export class WEAS extends CComponent { /** * converts calculated output property number-array to string-associative-array * @param {ArrayLike} dProps processed properties - * @return {Object} + * @return {any} * @ignore */ private getProps(dProps: ArrayLike) { @@ -313,7 +356,7 @@ export class WEAS extends CComponent { const key = keys[index]; res[key] = dProps[PropIDs[key]]; } - return res; + return res as any; } /** @@ -416,9 +459,30 @@ export class WEAS extends CComponent { if (this.lastAudio && this.lastAudio.data) { this.context2.fillStyle = 'rgb(0,255,0)'; for (let index = 0; index < this.lastAudio.data.length; index++) { - const height = this.canvas1.height * this.lastAudio.data[index] / 2; - this.context2.fillRect(barWidth * index, this.canvas1.height - height, barWidth, height); + const height = this.canvas2.height * this.lastAudio.data[index] / 2; + this.context2.fillRect(barWidth * index, this.canvas2.height - height, barWidth, height); } + /* + // draw average, min & max lines + this.drawHorizontLine('rgb(255,255,0)', this.lastAudio.average); + this.drawHorizontLine('rgb(255,0,255)', this.lastAudio.max); + this.drawHorizontLine('rgb(127,127,127)', this.lastAudio.min); + */ } } + + /** + * Draww Context2 line + * @param {string} style + * @param {number} y + */ + private drawHorizontLine(style, y) { + const ctx = this.context2; + const cvs = this.canvas2; + ctx.fillStyle = style; + ctx.moveTo(0, cvs.height * y); + ctx.lineTo(cvs.width, cvs.height * y); + ctx.closePath(); + ctx.stroke(); + } } diff --git a/src/weas/WEAS.wasm.asc b/src/weas/WEAS.wasm.asc index 7597968..0e67a02 100644 --- a/src/weas/WEAS.wasm.asc +++ b/src/weas/WEAS.wasm.asc @@ -86,7 +86,7 @@ var peaks: f64 = 0; var diff: f64 = 0; var mlt: f64 = 0; var average: f64 = 0; -var silent: f64 = 0; +var silent: f64 = 1; var intensity: f64 = 0; var range: f64 = 0; @@ -136,34 +136,60 @@ function invertAll(data: Float64Array): void { } } +var emult: f64; + // function for processing actual change function equalizePeaks(array: Float64Array): void { - - smoothArray(peakData, 2); - smoothArray(minData, 3); + oldMax = 0.001; + newMax = 0.001; for (i = 0; i < DAT_LEN; i++) { - // constant but slow regression - peakData[i] *= 0.96; - // peak now? - if(array[i] > peakData[i]) - peakData[i] = array[i] * 0.9; - // min calc - minData[i] *= 0.5; - // min now? - if(array[i] < minData[i]) - minData[i] = array[i] * 0.1; - else - minData[i] += (array[i] - minData[i]) / 10; + // get old max + if (array[i] > oldMax) oldMax = array[i]; + + // peak edging + if(array[i] >= peakData[i]) { + peakData[i] = array[i]; + peakStrength[i] = 16; + } + else { + if(peakStrength[i] < 1) peakData[i] *= 0.97; + else peakStrength[i] -= (peakData[i] - array[i]); + } + // min edging + if(array[i] <= minData[i]) { + minData[i] = array[i] * 0.9; + minStrength[i] = 16; + } + else { + if(minStrength[i] < 1) minData[i] *= 1.01; + else minStrength[i] -= (array[i] - minData[i]); + } + + // calculate anti dynamics -> high frequencies = more + tmpF = 0.3 + ((i as f64) / (DAT_LEN as f64)) * 0.4; + // scale equalize - array[i] = (array[i] + (array[i] - minData[i]) / (peakData[i] - minData[i])) / 2; + emult = (array[i] - minData[i]) / (peakData[i] - minData[i]); + array[i] = array[i] * (1.0 - tmpF) + (emult < 0 ? 0 : (emult > 1.2 ? 1.2 : emult)) * tmpF; + + // new max + if (array[i] > newMax) newMax = array[i]; } + + // re-scale & apply + tmpF = newMax / oldMax; + for (i = 0; i < DAT_LEN; i++) + array[i] = tempArr[i] / tmpF; + + smoothArray(peakData, 1); + smoothArray(minData, 1); } // filter peaks for full range function peakFilter(array: Float64Array, amount: f64): void { - oldMax = 0; - newMax = 0; + oldMax = 0.001; + newMax = 0.001; // pow this shit for (i = 0; i < DAT_LEN; i++) { if (array[i] > oldMax) oldMax = array[i]; @@ -171,7 +197,7 @@ function peakFilter(array: Float64Array, amount: f64): void { if (tempArr[i] > newMax) newMax = tempArr[i]; } // re-scale & apply - tmpF = newMax / oldMax / 1.25; + tmpF = newMax / oldMax; for (i = 0; i < DAT_LEN; i++) array[i] = tempArr[i] / tmpF; } @@ -180,11 +206,12 @@ function peakFilter(array: Float64Array, amount: f64): void { function smoothArray(array: Float64Array, steps: i32): void { // make smoothed array for (i = 0; i < DAT_LEN; i++) { - tmpF = 0; + tmpF = 0.001; for (var inner = i - steps; inner <= i + steps; inner++) { var idx = inner; - if (idx < 0) idx += DAT_LEN; - tmpF += array[idx % DAT_LEN]; + if (idx < 0) idx = 0; + if (idx >= DAT_LEN) idx = DAT_LEN - 1; + tmpF += array[idx]; } tempArr[i] = tmpF / (((steps * 2) + 1) as f64); } @@ -214,7 +241,9 @@ const workData = new Float64Array(DAT_LEN); // equalize helper const peakData = new Float64Array(DAT_LEN); +const peakStrength = new Float64Array(DAT_LEN); const minData = new Float64Array(DAT_LEN); +const minStrength = new Float64Array(DAT_LEN); // PROCESSED AUDIO DATA // either: B-H | H-B | HL-BL-BR-HR | BL-HL-HR-BR @@ -231,12 +260,20 @@ export const audioSettings = new Float64Array(11); // this will update and process new data export function update(): void { + // reset minimum data + if(isOn(silent)) { + peakData.fill(0.0); + peakStrength.fill(8.0); + minData.fill(1.0); + minStrength.fill(8.0); + } + // copy the input data to a working buffer // so the input could be updated in the meantime workData.set(inputData); - // equalize 1st step - if (isOn(audioSettings[0])) + // equalize disabled, correct pink noise instead + if (!isOn(audioSettings[0])) correctPinkNoise(workData); // write botch channels to mono @@ -261,7 +298,7 @@ export function update(): void { } } - // equalize 2nd step + // dynamic equalize if (isOn(audioSettings[0])) { equalizePeaks(workData); } @@ -275,9 +312,10 @@ export function update(): void { smoothArray(workData, Math.floor(audioSettings[4]) as i32); // process with last data - applyValueLeveling(workData, outputData, - audioSettings[5], - audioSettings[6]); + if(audioSettings[5] < 100 || audioSettings[6] < 100) + applyValueLeveling(workData, outputData, + audioSettings[5], + audioSettings[6]); // process current frequency data sum = 0; @@ -294,7 +332,7 @@ export function update(): void { if (tmpF < min) min = tmpF; if (tmpF > max) max = tmpF; // process ranges - if (indx < (42 / 2)) bass += tmpF * audioSettings[9]; + if (indx < 16) bass += tmpF * audioSettings[9]; else if (indx > 69) peaks += tmpF * audioSettings[7]; else mids += tmpF * audioSettings[8]; // calc peak average @@ -302,9 +340,14 @@ export function update(): void { } // calc average with previous entry - average = sum / (DAT_LEN as f64); + average = (sum + 0.01)/ (DAT_LEN as f64); silent = (max < audioSettings[10] / 1000) ? 1 : 0; - intensity = (bass * 8 + mids + peaks) / 8 / (average + 0.01); + + intensity = (bass * 8 - mids + peaks) / 4 * average; + for (indx = 0; indx < DAT_LEN; indx++) { + if(workData[indx] > average) intensity += (workData[indx] - average) * 0.5; + } + range = max - min; // Apply Data diff --git a/src/weas/index.ts b/src/weas/index.ts new file mode 100644 index 0000000..1de0d39 --- /dev/null +++ b/src/weas/index.ts @@ -0,0 +1,12 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +* +*/ + +export * from './WEAS'; +export * from './Bea'; From 64e3b2edc0ac65a8e5c0f269d13d80f8751feae5 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Mon, 18 Oct 2021 12:59:56 +0200 Subject: [PATCH 39/76] update images --- src/WarnHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index e6a1407..a50cb68 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -12,7 +12,7 @@ import {CSettings} from './CSettings'; import {waitReady} from './Util'; const ELM_ID = 'triggerwarn'; -const IMG_SRC = './img/triggerwarning.png'; +const IMG_SRC = './img/triggerwarn.png'; /** * Seizure display warnings From 7177e05184145f76e1ae5193dc72186548ddb29c Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Mon, 18 Oct 2021 13:04:48 +0200 Subject: [PATCH 40/76] small render, audio & debug improvements --- index.ts | 2 +- src/CSettings.ts | 2 +- src/FPSta.ts | 40 +++++++++++++++++---------- src/ReloadHelper.ts | 2 +- src/WEICUE.ts | 2 +- src/offline/OfflineHelper.ts | 10 ++++--- src/renamer/RenamerPlugin.js | 2 +- src/three/EffectComposer.ts | 53 ++++++++++-------------------------- src/three/pass/RenderPass.ts | 2 +- 9 files changed, 52 insertions(+), 63 deletions(-) diff --git a/index.ts b/index.ts index a705875..dfae3e1 100644 --- a/index.ts +++ b/index.ts @@ -14,7 +14,7 @@ export * from './src/FPSta'; export * from './src/ReloadHelper'; export * from './src/Util'; export * from './src/WarnHelper'; -export * from './src/WEAS'; +export * from './src/weas'; export * from './src/WEICUE'; export * from './src/WEWA'; diff --git a/src/CSettings.ts b/src/CSettings.ts index 2e781c2..1f148dc 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -36,7 +36,7 @@ export class CSettings { if (this[key] !== castedValue) { this[key] = castedValue; return true; - } + } else Smallog.debug('CSettings value not changed: ' + key + ' = ' + castedValue); } else { Smallog.error('CSettings Error: invalid type on: \'' + key + '\'. Is: \'' + typeof this[key] + '\', applied: \'' + typeof castedValue + '\''); diff --git a/src/FPSta.ts b/src/FPSta.ts index f3e4415..8840328 100644 --- a/src/FPSta.ts +++ b/src/FPSta.ts @@ -9,9 +9,8 @@ import {CComponent} from './CComponent'; import {CSettings} from './CSettings'; -import {Smallog} from './Smallog'; import {waitReady} from './Util'; -import {WEAS} from './WEAS'; +import {WEAS} from './weas'; const ELM_ID = 'fpstats'; @@ -53,6 +52,7 @@ export class FPStats extends CComponent { private auProvider: WEAS = null; private audHolder: HTMLElement; private audioMS: number = 0; + private bpmHolder: HTMLDivElement; /** @@ -107,22 +107,26 @@ export class FPStats extends CComponent { // fps this.fpsHolder = document.createElement('div'); this.fpsHolder.innerText = 'FPS: 0'; - // usage - this.useHolder = document.createElement('div'); - this.useHolder.innerText = 'Usage: 0 %'; // cpu this.cpuHolder = document.createElement('div'); - this.cpuHolder.innerText = 'CPU: 0 ms'; + this.cpuHolder.innerText = 'CPU: 0.00 ms'; // gpu this.gpuHolder = document.createElement('div'); - this.gpuHolder.innerText = 'GPU: 0 ms'; + this.gpuHolder.innerText = 'GPU: 0.00 ms'; + // usage + this.useHolder = document.createElement('div'); + this.useHolder.innerText = 'All: 0.00%'; // append - this.container.append(this.fpsHolder, this.useHolder, this.cpuHolder, this.gpuHolder); + this.container.append(this.fpsHolder, this.cpuHolder, this.gpuHolder, this.useHolder); // audio if (this.auProvider) { + this.bpmHolder = document.createElement('div'); + this.bpmHolder.innerText = 'BPM: 0 ~ '; + this.audHolder = document.createElement('div'); this.audHolder.innerText = 'Audio: 0 ms'; - this.container.append(this.audHolder); + + this.container.append(this.bpmHolder, this.audHolder); } } @@ -183,18 +187,24 @@ export class FPStats extends CComponent { // calculate const elapsd = (now - this.lastUpdate) / 1000; const efpies = this.frameCount / elapsd; - const yusage = (this.cpuMS + this.gpuMS) / 900; + const yusage = (this.cpuMS + this.gpuMS) / 500; const cepeyu = this.cpuMS / this.frameCount; const gepeyu = this.gpuMS / this.frameCount; // apply - const da = this.fpsHolder.innerText = `FPS: ${efpies.toFixed(2)}`; - const db = this.useHolder.innerText = `Usage: ${yusage.toFixed(2)} %`; - const dc = this.cpuHolder.innerText = `CPU: ${cepeyu.toFixed(2)} ms`; - const dd = this.gpuHolder.innerText = `GPU: ${gepeyu.toFixed(2)} ms`; - Smallog.debug(`${da} ${db} ${dc} ${dd}`, '[FPStats]'); + this.fpsHolder.innerText = `FPS: ${efpies.toFixed(2)}`; + this.cpuHolder.innerText = `CPU: ${cepeyu.toFixed(2)} ms`; + this.gpuHolder.innerText = `GPU: ${gepeyu.toFixed(2)} ms`; + this.useHolder.innerText = `All: ${yusage.toFixed(2)} %`; if (this.audHolder) this.audHolder.innerText = `Audio: ${this.audioMS.toFixed(2)} ms`; + if (this.bpmHolder && this.auProvider.lastAudio && this.auProvider.lastAudio.bpm instanceof Array) { + let bts = 0; + const bp = this.auProvider.lastAudio.bpm; + bp.forEach((b) => bts += b.value); + bts /= bp.length; + this.bpmHolder.innerText = `BPM: ${bts.toFixed(2)} ~`; + } this.lastUpdate = now; this.reset(); diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index 12e42c8..4f2ea94 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -109,7 +109,7 @@ export class ReloadHelper extends CComponent { } /** - * @Todo test: bar always reset to 0 on show ?? + * always reset bar to 0 * @public * @param {boolean} visible */ diff --git a/src/WEICUE.ts b/src/WEICUE.ts index dfc8a20..1a140ef 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -11,7 +11,7 @@ import {CComponent} from './CComponent'; import {CSettings} from './CSettings'; import {rgbToObj, waitReady} from './Util'; import {Smallog} from './Smallog'; -import {WEAS} from './WEAS'; +import {WEAS} from './weas'; import {ICUE} from './ICUE'; const IMG_SRC = './img/icue.png'; diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index 5652ce3..ad62b0a 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -13,6 +13,8 @@ import {Smallog} from '../Smallog'; // this should pack the serviceworker like a webwoker. import OfflineWorker from 'worker-loader!./Offline'; +const oh = '[OfflineHelper] '; + /** * @ignore * Helper class for loading and registering the ServiceWorker @@ -54,12 +56,12 @@ function register(name: string, worker: string = 'Offline.worker.js', oFile: str if ('serviceWorker' in navigator) { const workerPath = `${worker}?name=${name}&jsonPath=${oFile}`; await navigator.serviceWorker.register(workerPath, {scope: '/'}) - .then(() => Smallog.info('service-worker registration complete.', '[OfflineHelper] '), - () => Smallog.error('service-worker registration failure.', '[OfflineHelper] ')) + .then(() => Smallog.info('service-worker registration complete.', oh), + () => Smallog.error('service-worker registration failure.', oh)) .then(() => resolve(true)); return true; } else { - Smallog.error('not supported!', '[OfflineHelper] '); + Smallog.error('not supported!', oh); resolve(false); } }); @@ -80,7 +82,7 @@ async function reset(): Promise { resolve(true); }); } else { - Smallog.error('not supported!', '[OfflineHelper] '); + Smallog.error('not supported!', oh); resolve(false); } }); diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js index c40bebd..2c911b1 100644 --- a/src/renamer/RenamerPlugin.js +++ b/src/renamer/RenamerPlugin.js @@ -52,7 +52,7 @@ class RenamerPlugin { * @return {string} */ getRandomName(src) { - const gen = 'hx' + Math.random().toString(36).substr(2, 3) + Math.floor(10 + Math.random() * 89); + const gen = '$x' + Math.random().toString(20).substr(2, 2 + Math.random() * 4); let exist = (src || '').indexOf(gen) >= 0; this.nameMap.forEach((mping) => { if (mping.key === gen) exist = true; diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts index 223cf0a..8adf7eb 100644 --- a/src/three/EffectComposer.ts +++ b/src/three/EffectComposer.ts @@ -7,7 +7,6 @@ import {LinearFilter, PerspectiveCamera, Quaternion, RGBAFormat, Scene, Vector2, WebGLRenderer, WebGLRenderTarget, XRFrame} from 'three'; import {BasePass} from './pass/BasePass'; import {RenderPass} from './pass/RenderPass'; -import {ShaderPass} from './pass/ShaderPass'; const defaultParams = { minFilter: LinearFilter, @@ -88,8 +87,8 @@ export class EffectComposer { this.previousFrame = Date.now(); this.globalPrecision = globalPrec; - this.normPass = new RenderPass(scene, camera, null, clearCol, 1); - this.xrPass = new RenderPass(scene, this.xrCam, null, clearCol, 1); + this.normPass = new RenderPass(scene, camera, null, clearCol); + this.xrPass = new RenderPass(scene, this.xrCam, null, clearCol); // @TODO == 1 } /** @@ -103,10 +102,9 @@ export class EffectComposer { /** * Append a shader to the chain * @public - * @param {BasePass} pass Shader to add + * @param {BasePass} p Shader to add */ - public addPass(pass: BasePass) { - const p = this.wrapPrecision(pass); + public addPass(p: BasePass) { p.setSize(this.viewSize.width, this.viewSize.height); this.passes.push(p); } @@ -114,15 +112,23 @@ export class EffectComposer { /** * Insert a shader in the chain * @public - * @param {BasePass} pass Shader to add + * @param {BasePass} p Shader to add * @param {number} index position */ - public insertPass(pass: BasePass, index: number) { - const p = this.wrapPrecision(pass); + public insertPass(p: BasePass, index: number) { p.setSize(this.viewSize.width, this.viewSize.height); this.passes.splice(index, 0, p); } + /** + * Update clear color + * @param {any} clearCol Color + */ + public setClearColor(clearCol: any) { + this.normPass.clearColor = clearCol; + this.xrPass.clearColor = clearCol; + } + /** * Checks if the given shader should be rendererd to screen * @param {number} passIndex position @@ -271,35 +277,6 @@ export class EffectComposer { /* UTILS */ - /** - * Prefixes custom WebGL-precision to shaders - * - * @param {BasePass} pass Shader to Wrap - * @return {BasePass} - * @ignore - */ - private wrapPrecision(pass: BasePass): BasePass { - if (pass instanceof ShaderPass) { - const copy = pass as ShaderPass; - // get prefix - let pre = 'precision ' + this.globalPrecision + ' float;\r\n ' + - 'precision ' + this.globalPrecision + ' int;\r\n '; - // "medium" sampler precision should always be available for "high" float precision. - if (this.globalPrecision == 'highp') { - pre += 'precision mediump sampler2D;\r\n ' + - 'precision mediump samplerCube;\r\n '; - } - // apply it - if (copy.material.vertexShader) { - copy.material.vertexShader = pre + copy.material.vertexShader; - } - if (copy.material.fragmentShader) { - copy.material.fragmentShader = pre + copy.material.fragmentShader; - } - } - return pass; - } - /** * Some shaders write to Input rather than Output... * diff --git a/src/three/pass/RenderPass.ts b/src/three/pass/RenderPass.ts index b027b28..8672e78 100644 --- a/src/three/pass/RenderPass.ts +++ b/src/three/pass/RenderPass.ts @@ -34,7 +34,7 @@ export class RenderPass implements BasePass { * @param {Color} clearColor * @param {number} clearAlpha */ - constructor(scene: Scene, camera: Camera, overMat: Material, clearColor, clearAlpha: number) { + constructor(scene: Scene, camera: Camera, overMat: Material, clearColor?, clearAlpha?: number) { this.scene = scene; this.camera = camera; From 35ede5086196fee43983df5ba9cfea70a85a8238 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Mon, 18 Oct 2021 13:05:09 +0200 Subject: [PATCH 41/76] update subproject --- src/wasc-worker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasc-worker b/src/wasc-worker index a14ae81..51ab435 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit a14ae811e08b87772df48bc836358427414e7bd1 +Subproject commit 51ab43515cfcd5412b0425ea8022b4c036c75658 From b113d40bd65f5d710542b1144cc69202d3515e1f Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Tue, 19 Oct 2021 18:09:38 +0200 Subject: [PATCH 42/76] implement accessor shortening --- src/renamer/RenamerPlugin.js | 221 ++++++++++++++++++++++++++++++++++- 1 file changed, 216 insertions(+), 5 deletions(-) diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js index 2c911b1..21edaf1 100644 --- a/src/renamer/RenamerPlugin.js +++ b/src/renamer/RenamerPlugin.js @@ -80,12 +80,223 @@ class RenamerPlugin { } /** - * replace all regex matches with random variable names - * @param {string} source - * @return {string} - */ + * Illegal JS accessor shortening + * @param {string} pd src + * @return {string} res + */ + shortenAccessors(pd) { + const candids = '色 彩 就 是 空 虚 纸 张 图 片'.split(' '); + let globalPre = candids[0]; + let candI = 1; + + // @todo from settings + const blackList = ['.arguments', + '.__getFloat64ArrayView', '.__getInt32Array', '.__getFloat32ArrayView']; + + // count all accessor usages, which could gain an advantage by shortening + const processed = {}; + pd.match(/\.+[a-zA-Z0-9_]{5,}/g).forEach((element) => { + const match = element; + if (match.indexOf('.') != 0) return; // only dot at start allowed + if (match.indexOf('..') == 0) return; // skip spread operator + if (blackList.includes(match)) return; + + if (processed[match]) processed[match]++; + else processed[match] = 1; + }); + + // convert & calculate the actual 'char' savings + const converted = Object.keys(processed).map((key) => { + const val = processed[key]; + const saving = val * (key.length) - (key.length + 3); + return {k: key, v: val, w: saving}; + }); + converted.sort((b, a) => (a.w > b.w) ? 1 : ((b.w > a.w) ? -1 : 0)); + + // filter positive savings only + const globalMap = {}; + globalMap[globalPre] = []; + + const filtered = []; + let realI = 0; + let canCount = 0; + const minSum = (globalPre.length + 8); // 'var =[];' + let sum = - minSum; + for (let i = 0; i < converted.length; i++) { + const element = converted[i]; + + if (canCount > 99) { + globalPre = candids[candI++ % candids.length]; + if (!globalMap[globalPre]) globalMap[globalPre] = []; + realI = globalMap[globalPre].length; + canCount = 0; + sum -= minSum; + } + + const replacement = '[' + globalPre + '[' + realI + ']]'; + const newLen = element.v * replacement.length; + const newWeight = element.w - newLen; + // if the advantage exceeds the own weight, process it + if (newWeight > element.k.length) { + globalMap[globalPre][realI++] = element.k.substring(1); + canCount++; + filtered.push({k: element.k, v: element.v, w: newWeight, r: replacement}); + sum += newWeight; + } + } + + // !! important to replace the longest words first, so we dont have partial replacements !! + filtered.sort((b, a) => (a.k.length > b.k.length) ? 1 : ((b.k.length > a.k.length) ? -1 : 0)); + + // console.log(JSON.stringify(filtered, null, '\t')); + console.log('Potentially saving: ' + sum + ' chars.'); + + const oldPd = pd; + const oldStrict ='"use strict";'; + + + // replace all occurences + for (let index = 0; index < filtered.length; index++) { + const element = filtered[index]; + console.log('Replacing ' + element.k + ' => ' + element.r + ' (' + element.v + ' usages)'); + + let skipped = 0; + let newPd = pd; + + // check & queue every potential replacement one-by-one + const operations = []; + const rgx = new RegExp('\\' + element.k, 'g'); + let rm; + while (( rm = rgx.exec(newPd)) != null) { + const rIdx = rm.index; + + // match is invalid if it is followed by a alphanumeric char or _ + const followChar = newPd[rgx.lastIndex]; + let isInvalid = (followChar.match(/[a-zA-Z0-9_]/) != null); + + // check if replacement is in "" string + // check if replacement is in '' string + // check if replacement is in `` string + const patt = /'((?:\\.|[^'])*)'|"((?:\\.|[^"])*)"|`((?:\\.|[^`])*)`/igm; + let match; + while ((match = patt.exec(newPd)) != null && !isInvalid) { + // console.log(match.index + ' ' + patt.lastIndex); + if (rIdx >= match.index && rIdx <= patt.lastIndex) { + isInvalid = true; + // console.log(`'${element.k}' Is in String between [${pIdx}:${pIdx + pLen}] at [${rIdx}:${rIdx + rLen}] ==> ${newPd.substring(rIdx - rLen -5, rIdx + 5)}`); + } + // else console.log(`${element.k} not in ${match[0]}`); + } + + if (!isInvalid) { + operations.push({start: rIdx, end: rgx.lastIndex, repl: element.r}); + } else { + skipped++; + } + } + + // execute pending operations + for (let index = 0; index < operations.length; index++) { + const operation = operations[index]; + const from = operation.start; + const to = operation.end; + const re = operation.repl; + + // console.log(`Replace '${newPd.substring(from, to)}' at ${from} with '${re}' ==> ${newPd.substring(from -5, to + 5)}`); + newPd = newPd.substring(0, from) + re + newPd.substring(to); + + // calculate new offset for following pending operations + const off = (to - from) - re.length; + for (let idx = index +1; idx < operations.length; idx++) { + const op = operations[idx]; + if (op.start > to - off) op.start -= off; + if (op.end > to - off) op.end -= off; + } + } + + // sanity check + // @todo bruh + if (newPd.match(/\]\][a-zA-Z0-9_]{1,}/)) { + console.log('Error double bracket, missing delimiter? '); + console.log(element); + } else { + if (skipped > 0) console.log(`Skipped ${skipped} invalid replacements.`); + pd = newPd; + } + } + + + // prepend all global Maps + + // @todo use string join / split if count > X ? + Object.keys(globalMap).forEach((k) => { + let val = globalMap[k]; + if (val.length > 4) { + val = `"${val.join('.')}".split('.')`; + } else { + val = JSON.stringify(val); + } + const newStrict = 'var ' + k + '=' + val + ';'; + pd = pd.replace(oldStrict, oldStrict + newStrict); + }); + + const lengDiff = oldPd.length - pd.length; + const expDiff = lengDiff - sum; + + console.log('Replacement complete!'); + let m = 'Actually saved: ' + lengDiff + ' chars. '; + if (expDiff > 0) m+= ' (' + expDiff + ' more than expected)'; + if (expDiff < 0) m+= ' (' + Math.abs(expDiff) + ' less than expected)'; + console.log(m); + if (lengDiff < 0) { + console.log('Did not save any chars! rolling back...'); + pd = oldPd; + } + + return pd; + } + + /** + * process via regex + * @param {string} source + * @return {string} + */ processString(source) { - return source.replace(this.options.regex, (match) => this.replaceMatch(source, match)); // .replaceAll('const ', 'var '); + const pd = source.replace(this.options.regex, (match) => this.replaceMatch(source, match)); // .replaceAll('const ', 'var '); + + const sa = this.shortenAccessors(pd); + + return sa; + } + + + /** Function that count occurrences of a substring in a string; + * @param {String} string The string + * @param {String} subString The sub string to search for + * @param {Boolean} [allowOverlapping] Optional. (Default:false) + * @return {number} + * + * @author Vitim.us https://gist.github.com/victornpb/7736865 + * @see Unit Test https://jsfiddle.net/Victornpb/5axuh96u/ + * @see http://stackoverflow.com/questions/4009756/how-to-count-string-occurrence-in-string/7924240#7924240 + */ + occurrences(string, subString, allowOverlapping) { + string += ''; + subString += ''; + if (subString.length <= 0) return (string.length + 1); + + let n = 0; + let pos = 0; + const step = allowOverlapping ? 1 : subString.length; + + while (true) { + pos = string.indexOf(subString, pos); + if (pos >= 0) { + ++n; + pos += step; + } else break; + } + return n; } /** From 11220e551eba9b9d705fcff52769740ebcd7f999 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 23 Oct 2021 10:09:28 +0200 Subject: [PATCH 43/76] working preparation for three.ts --- .gitmodules | 3 +++ src/three.ts | 1 + 2 files changed, 4 insertions(+) create mode 160000 src/three.ts diff --git a/.gitmodules b/.gitmodules index d18a868..905929c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "src/wasc-worker"] path = src/wasc-worker url = https://github.com/hexxone/wasc-worker.git +[submodule "src/three.ts"] + path = src/three.ts + url = https://github.com/hexxone/three.ts diff --git a/src/three.ts b/src/three.ts new file mode 160000 index 0000000..cd8c225 --- /dev/null +++ b/src/three.ts @@ -0,0 +1 @@ +Subproject commit cd8c225742dbc275403ae272a61ab1d162f36c7a From 7abd24805c6c84621d1a09ec217e7c9eb1cd9981 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 23 Oct 2021 10:09:35 +0200 Subject: [PATCH 44/76] working preparation for three.ts --- src/Smallog.ts | 2 +- src/WEWA.ts | 166 +++++++++----- src/renamer/RenamerPlugin.js | 105 +++++---- src/renamer/jsfuck.js | 368 +++++++++++++++++++++++++++++++ src/three.ts | 2 +- src/three/shader/loader/index.js | 13 +- src/weas/WEAS.ts | 6 +- 7 files changed, 557 insertions(+), 105 deletions(-) create mode 100644 src/renamer/jsfuck.js diff --git a/src/Smallog.ts b/src/Smallog.ts index cd04049..250e51d 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -34,7 +34,7 @@ export enum LogLevel { * @public */ class Smalog { - logLevel: LogLevel = LogLevel.Info; + logLevel: LogLevel = LogLevel.Debug; preFix: string = '[Smallog] '; printTime: boolean = false; diff --git a/src/WEWA.ts b/src/WEWA.ts index 04d63d7..026c6e4 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -14,6 +14,8 @@ import {WascUtil} from './wasc-worker/WascUtil'; const LogHead = '[WEWWA] '; const DefLang = 'de-de'; +const wral = 'wallpaperRegisterAudioListener'; +const proj = 'project.json'; /** * WEWWA @@ -77,7 +79,7 @@ export class WEWWA { private htmlIcon: Element = null; private audio: HTMLAudioElement = null; - private ctx: any = null; + private ctx: AudioContext = null; private source: any = null; private analyser: any = null; @@ -93,7 +95,7 @@ export class WEWWA { * @param {Function} finished Callback for initializing the wallpaper */ constructor(finished) { - if (window['wallpaperRegisterAudioListener']) { + if (window[wral]) { Smallog.info('detected wallpaper engine => Standby.', LogHead); finished(); return; @@ -102,7 +104,7 @@ export class WEWWA { Smallog.info('wallpaper engine not detected => Init!', LogHead); // define audio listener first, so we dont miss when it gets registered. - window['wallpaperRegisterAudioListener'] = (callback) => { + window[wral] = (callback) => { // set callback to be called later with analysed audio data this.audioCallback = callback; Smallog.info('Registered wallpaper AudioListener.', LogHead); @@ -128,11 +130,14 @@ export class WEWWA { * @ignore */ private init() { - WascUtil.myFetch('project.json', 'json').then((proj) => { + WascUtil.myFetch(proj, 'json').then((proj) => { if (proj.type != 'web') { - Smallog.error('Error! Loaded project.json is not a web Wallpaper. How did this happen? Aborting...', LogHead); + Smallog.error(`Error! Loaded ${proj} is not a web Wallpaper. How did this happen? Aborting...`, LogHead); return; } + + // new NDBG(); + this.project = proj; this.loadStorage(); this.addStyle(); @@ -570,32 +575,40 @@ export class WEWWA { aBtn1.classList.add('audio'); aBtn1.innerHTML = 'Microphone'; aBtn1.addEventListener('click', (e) => { - this.requestMicrophone(); + this.initMicrophone(); }); - // File Url input + // Desktop Audio input const aBtn2 = ce('a'); aBtn2.classList.add('audio'); - aBtn2.innerHTML = 'Select URL'; + aBtn2.innerHTML = 'Desktop Audio (Chrome)'; aBtn2.addEventListener('click', (e) => { + this.initDesktop(); + }); + + // File Url input + const aBtn3 = ce('a'); + aBtn3.classList.add('audio'); + aBtn3.innerHTML = 'Select URL'; + aBtn3.addEventListener('click', (e) => { const uri = prompt('Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!', 'https://example.com/test.mp3'); - this.initiateAudio(uri); + this.initFile(uri); }); // System file input - const aBtn3 = ce('input'); - aBtn3.id = 'wewwaAudioInput'; - aBtn3.innerHTML = 'Select File'; - aBtn3.setAttribute('type', 'file'); - aBtn3.addEventListener('change', (e) => { + const aBtn4 = ce('input'); + aBtn4.id = 'wewwaAudioInput'; + aBtn4.innerHTML = 'Select File'; + aBtn4.setAttribute('type', 'file'); + aBtn4.addEventListener('change', (e) => { const file = (e.target as any).files[0]; if (!file) { return; } - this.initiateAudio(file); + this.initFile(file); }); - td1.append(aBtn1, aBtn2, aBtn3); + td1.append(aBtn1, aBtn2, aBtn3, aBtn4); row.append(td1); @@ -617,7 +630,7 @@ export class WEWWA { e.stopPropagation(); e.preventDefault(); const droppedFiles = e.dataTransfer.files; - this.initiateAudio(droppedFiles[0]); + this.initFile(droppedFiles[0]); }, false); dropCol1.append(dropArea); dropRow.append(dropCol1, dropCol2); @@ -650,6 +663,7 @@ export class WEWWA { private addMenuHeader(ce: (e: any) => any, proj: any, menu = this.htmlMenu) { const preview = ce('img'); preview.setAttribute('src', proj.preview); + preview.setAttribute('alt', 'Steam Workshop Preview Image'); // create menu app title const header = ce('div'); header.innerHTML = '

      ' + proj.title + '

      '; @@ -881,7 +895,10 @@ export class WEWWA { * @public */ public evaluateSettings() { + // dynamic prefix for evaluation + const pre = 'wewwaProps'; const wewwaProps = this.project.general.properties; + localStorage.setItem('wewwaLastProps', JSON.stringify(wewwaProps)); for (const p in wewwaProps) { if (!p) continue; @@ -896,7 +913,7 @@ export class WEWWA { const partials = cprop.split(/&&|\|\|/); // loop all partial values of the check for (const part of partials) { - let prefix = 'wewwaProps.'; + let prefix = pre + '.'; const onlyVal = part.match(/[!a-zA-Z0-9_\.]*/)[0]; if (!onlyVal.startsWith(prefix) && !onlyVal.startsWith('!' + prefix)) { // fix for inverted values @@ -910,7 +927,7 @@ export class WEWWA { } } try { - visible = eval(cprop) == true; + visible = new Function(pre, 'return ('+cprop+')')(wewwaProps) === true; } catch (e) { Smallog.error('Error: (' + cprop + ') for: ' + p + ' => ' + e, LogHead); } @@ -1003,46 +1020,43 @@ export class WEWWA { * Request microphone from browser * @ignore */ - private requestMicrophone() { - navigator.mediaDevices.getUserMedia({ + private initMicrophone() { + const md = navigator.mediaDevices as any; + if (!md['getUserMedia']) return; + md.getUserMedia({ audio: true, }).then((stream) => { + Smallog.debug('Got Microphone MediaStream!'); + // stop previous analyzer this.stopAudioInterval(); - // hack for firefox to keep stream running - window['persistAudioStream'] = stream; - this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); - this.source = this.ctx.createMediaStreamSource(stream); - this.analyser = this.ctx.createAnalyser(); - this.analyser.smoothingTimeConstant = 0.15; - this.analyser.fftSize = 256; - - this.source.connect(this.analyser); - this.startAudioInterval(); + this.makeAnalyzer(stream); }).catch((err) => { Smallog.error(err, LogHead); if (location.protocol != 'https:') { - const r = confirm('Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS for this feature to work! Press \'ok\'/\'yes\' to get redirected to HTTPS and try again.'); + const r = confirm('Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS! Press \'ok\'/\'yes\' to get redirected from HTTP => HTTPS.'); if (r) window.location.href = window.location.href.replace('http', 'https'); } }); } - // eslint-disable-next-line valid-jsdoc /** - * html5 audio analyser gives us mono data from 0(bass) to 128(treble) - * however, wallpaper engine expects stereo data in following format: - * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) - * so we do some array transformation... and divide by 255 (8bit-uint becomes float) - * @ignore + * Initiate Desktop auddio streaming */ - private convertAudio(data) { - const stereo = []; - let sIdx = 0; - for (let i = 0; i < 64; i++) { - stereo[i] = data[sIdx++] / 255; - stereo[64 + i] = data[sIdx++] / 255; - } - return stereo; + private async initDesktop() { + const md = navigator.mediaDevices as any; + if (!md['getDisplayMedia']) return; + md.getDisplayMedia({ + video: true, + audio: true, + }).then((stream) => { + Smallog.debug('Got Desktop MediaStream!'); + // stop previous analyzer + this.stopAudioInterval(); + this.makeAnalyzer(stream); + }).catch((e) => { + Smallog.error('Cant Open Desktop Audio Stream!', '[WEWA] '); + console.error(e); + }); } // eslint-disable-next-line valid-jsdoc @@ -1050,28 +1064,50 @@ export class WEWWA { * Start the audio processing & analyzer * @ignore */ - private initiateAudio(data) { - // clear up + private initFile(file) { + // stop previous analyzer this.stopAudioInterval(); + if (!file) return; + // create player this.audio = document.createElement('audio'); - this.audio.src = data.name ? URL.createObjectURL(data) : data; + this.audio.src = file.name ? URL.createObjectURL(file) : file; this.audio.autoplay = true; this.audio.setAttribute('controls', 'true'); this.audio.play(); - // insert before marker const markr = document.getElementById('audioMarker'); markr.prepend(this.audio); - this.ctx = new (window.AudioContext || window['webkitAudioContext'])(); - this.source = this.ctx.createMediaElementSource(this.audio); - this.analyser = this.ctx.createAnalyser(); - this.analyser.smoothingTimeConstant = 0.1; - this.analyser.fftSize = 512; + this.makeAnalyzer(this.audio); + } - this.source.connect(this.ctx.destination); + /** + * + * @param {MediaStream} src + */ + private makeAnalyzer(src: MediaStream | HTMLAudioElement) { + // new context + this.ctx = new (window.AudioContext || window['webkitAudioContext'])({sampleRate: 48000}); + // microphone or desktop stream sauce + if (src instanceof MediaStream) { + this.source = this.ctx.createMediaStreamSource(src); + // hack for firefox to keep stream running + window['persistAudioStream'] = src; + } + // audio html element sauce + if (src instanceof HTMLAudioElement) { + this.source = this.ctx.createMediaElementSource(src); + // we want to hear this on our pc => connect source OUT to media IN + this.source.connect(this.ctx.destination); + } + // new analyzer + this.analyser = this.ctx.createAnalyser(); + this.analyser.smoothingTimeConstant = 0.05; + this.analyser.fftSize = 256; + // connect source OUT to analyzer IN this.source.connect(this.analyser); + // start analyzing this.startAudioInterval(); } @@ -1096,6 +1132,24 @@ export class WEWWA { this.applyProp({audioprocessing: {value: true}}); } + // eslint-disable-next-line valid-jsdoc + /** + * html5 audio analyser gives us mono data from 0(bass) to 128(treble) + * however, wallpaper engine expects stereo data in following format: + * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) + * so we do some array transformation... and divide by 255 (8bit-uint becomes float) + * @ignore + */ + private convertAudio(data) { + const stereo = []; + let sIdx = 0; + for (let i = 0; i < 64; i++) { + stereo[i] = data[sIdx++] / 255; + stereo[64 + i] = data[sIdx++] / 255; + } + return stereo; + } + /** * Stop the processing loop * @public diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js index 21edaf1..9fea7f1 100644 --- a/src/renamer/RenamerPlugin.js +++ b/src/renamer/RenamerPlugin.js @@ -8,6 +8,8 @@ * @ignore */ +const lib = require('./jsfuck.js'); + const {RawSource} = require('webpack-sources'); const validate = require('schema-utils'); @@ -79,18 +81,40 @@ class RenamerPlugin { return fnd; } + + /** + * randomize array + * @param {Array} array + * @return {Array} + */ + shuffle(array) { + let currentIndex = array.length; let randomIndex; + // While there remain elements to shuffle... + while (currentIndex != 0) { + // Pick a remaining element... + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex--; + // And swap it with the current element. + [array[currentIndex], array[randomIndex]] = [ + array[randomIndex], array[currentIndex]]; + } + return array; + } + /** * Illegal JS accessor shortening * @param {string} pd src * @return {string} res */ shortenAccessors(pd) { - const candids = '色 彩 就 是 空 虚 纸 张 图 片'.split(' '); + // 杀 屠 大 门 安 天 + const candids = this.shuffle('職 識 辦 辯 色 特 持 谁 准 彩 就 是 空 虚 纸 张 图 片 末 未 已 己 土 士 干 千 人 入'.split(' ')); let globalPre = candids[0]; let candI = 1; // @todo from settings - const blackList = ['.arguments', + const blackList = ['.arguments', '.update', '.outputData', '.audioProps', '.inputData', '.levelSettings', + '.exports', '.build', '.forEach', '.buffer', '.__getFloat64ArrayView', '.__getInt32Array', '.__getFloat32ArrayView']; // count all accessor usages, which could gain an advantage by shortening @@ -125,7 +149,7 @@ class RenamerPlugin { for (let i = 0; i < converted.length; i++) { const element = converted[i]; - if (canCount > 99) { + if (canCount > 9) { globalPre = candids[candI++ % candids.length]; if (!globalMap[globalPre]) globalMap[globalPre] = []; realI = globalMap[globalPre].length; @@ -227,19 +251,54 @@ class RenamerPlugin { // prepend all global Maps + const delim = '.'; + const splFunc = '大'; + const rndOff = Math.round(2 + Math.random() * 14); + + // eslint-disable-next-line require-jsdoc + function caesarCipher(s) { + let r = ''; + for (let i = 0; i < s.length; i++) { + r += String.fromCharCode((s[i].charCodeAt()) + rndOff); + } + return r; + } + + + let newStrict = oldStrict; + const keys = Object.keys(globalMap); + if (keys.length > 4) { + const fk1 = lib.JSFuck.encode(rndOff.toString()); + const fk2 = lib.JSFuck.encode('split'); + const fk3 = lib.JSFuck.encode(delim); + const func = `(s)=>{var r='';for(var i=0;i X ? - Object.keys(globalMap).forEach((k) => { + keys.forEach((k) => { let val = globalMap[k]; if (val.length > 4) { - val = `"${val.join('.')}".split('.')`; + if (keys.length > 4) { + val = `${splFunc}(${JSON.stringify(caesarCipher(val.join(delim)))})`; + } else { + val = `"${val.join(delim)}".split('${delim}')`; + } } else { val = JSON.stringify(val); } - const newStrict = 'var ' + k + '=' + val + ';'; - pd = pd.replace(oldStrict, oldStrict + newStrict); + if (newStrict.endsWith(')' || newStrict.endsWith('}'))) { + newStrict += ','; + } + if (newStrict.endsWith(';')) { + newStrict +='var '; + } + newStrict += `${k}=${val}`; }); + console.log(newStrict); + + pd = pd.replace(oldStrict, newStrict + ';'); + const lengDiff = oldPd.length - pd.length; const expDiff = lengDiff - sum; @@ -269,36 +328,6 @@ class RenamerPlugin { return sa; } - - /** Function that count occurrences of a substring in a string; - * @param {String} string The string - * @param {String} subString The sub string to search for - * @param {Boolean} [allowOverlapping] Optional. (Default:false) - * @return {number} - * - * @author Vitim.us https://gist.github.com/victornpb/7736865 - * @see Unit Test https://jsfiddle.net/Victornpb/5axuh96u/ - * @see http://stackoverflow.com/questions/4009756/how-to-count-string-occurrence-in-string/7924240#7924240 - */ - occurrences(string, subString, allowOverlapping) { - string += ''; - subString += ''; - if (subString.length <= 0) return (string.length + 1); - - let n = 0; - let pos = 0; - const step = allowOverlapping ? 1 : subString.length; - - while (true) { - pos = string.indexOf(subString, pos); - if (pos >= 0) { - ++n; - pos += step; - } else break; - } - return n; - } - /** * Hook into the compilation process, * Replace regex matches with random strings diff --git a/src/renamer/jsfuck.js b/src/renamer/jsfuck.js new file mode 100644 index 0000000..5a0e5ca --- /dev/null +++ b/src/renamer/jsfuck.js @@ -0,0 +1,368 @@ +/* eslint-disable guard-for-in */ +/* eslint-disable require-jsdoc */ +/* ! JSFuck 0.5.0 - http://jsfuck.com */ + +(function(self) { + const MIN = 32; const MAX = 126; + + const SIMPLE = { + 'false': '![]', + 'true': '!![]', + 'undefined': '[][[]]', + 'NaN': '+[![]]', + 'Infinity': '+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])', // +"1e1000" + }; + + const CONSTRUCTORS = { + 'Array': '[]', + 'Number': '(+[])', + 'String': '([]+[])', + 'Boolean': '(![])', + 'Function': '[]["flat"]', + 'RegExp': 'Function("return/"+false+"/")()', + 'Object': '[]["entries"]()', + }; + + const MAPPING = { + 'a': '(false+"")[1]', + 'b': '([]["entries"]()+"")[2]', + 'c': '([]["flat"]+"")[3]', + 'd': '(undefined+"")[2]', + 'e': '(true+"")[3]', + 'f': '(false+"")[0]', + 'g': '(false+[0]+String)[20]', + 'h': '(+(101))["to"+String["name"]](21)[1]', + 'i': '([false]+undefined)[10]', + 'j': '([]["entries"]()+"")[3]', + 'k': '(+(20))["to"+String["name"]](21)', + 'l': '(false+"")[2]', + 'm': '(Number+"")[11]', + 'n': '(undefined+"")[1]', + 'o': '(true+[]["flat"])[10]', + 'p': '(+(211))["to"+String["name"]](31)[1]', + 'q': '("")["fontcolor"]([0]+false+")[20]', + 'r': '(true+"")[1]', + 's': '(false+"")[3]', + 't': '(true+"")[0]', + 'u': '(undefined+"")[0]', + 'v': '(+(31))["to"+String["name"]](32)', + 'w': '(+(32))["to"+String["name"]](33)', + 'x': '(+(101))["to"+String["name"]](34)[1]', + 'y': '(NaN+[Infinity])[10]', + 'z': '(+(35))["to"+String["name"]](36)', + + 'A': '(NaN+[]["entries"]())[11]', + 'B': '(+[]+Boolean)[10]', + 'C': 'Function("return escape")()(("")["italics"]())[2]', + 'D': 'Function("return escape")()([]["flat"])["slice"]("-1")', + 'E': '(RegExp+"")[12]', + 'F': '(+[]+Function)[10]', + 'G': '(false+Function("return Date")()())[30]', + 'H': null, + 'I': '(Infinity+"")[0]', + 'J': null, + 'K': null, + 'L': null, + 'M': '(true+Function("return Date")()())[30]', + 'N': '(NaN+"")[0]', + 'O': '(+[]+Object)[10]', + 'P': null, + 'Q': null, + 'R': '(+[]+RegExp)[10]', + 'S': '(+[]+String)[10]', + 'T': '(NaN+Function("return Date")()())[30]', + 'U': '(NaN+Object()["to"+String["name"]]["call"]())[11]', + 'V': null, + 'W': null, + 'X': null, + 'Y': null, + 'Z': null, + + ' ': '(NaN+[]["flat"])[11]', + '!': null, + '"': '("")["fontcolor"]()[12]', + '#': null, + '$': null, + '%': 'Function("return escape")()([]["flat"])[21]', + '&': '("")["fontcolor"](")[13]', + '\'': null, + '(': '([]["flat"]+"")[13]', + ')': '([0]+false+[]["flat"])[20]', + '*': null, + '+': '(+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]])+[])[2]', + ',': '[[]]["concat"]([[]])+""', + '-': '(+(.+[0000001])+"")[2]', + '.': '(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]', + '/': '(false+[0])["italics"]()[10]', + ':': '(RegExp()+"")[3]', + ';': '("")["fontcolor"](NaN+")[21]', + '<': '("")["italics"]()[0]', + '=': '("")["fontcolor"]()[11]', + '>': '("")["italics"]()[2]', + '?': '(RegExp()+"")[2]', + '@': null, + '[': '([]["entries"]()+"")[0]', + '\\': '(RegExp("/")+"")[1]', + ']': '([]["entries"]()+"")[22]', + '^': null, + '_': null, + '`': null, + '{': '(true+[]["flat"])[20]', + '|': null, + '}': '([]["flat"]+"")["slice"]("-1")', + '~': null, + }; + + const GLOBAL = 'Function("return this")()'; + + function fillMissingDigits() { + let output; let number; let i; + + for (number = 0; number < 10; number++) { + output = '+[]'; + + if (number > 0) { + output = '+!' + output; + } + for (i = 1; i < number; i++) { + output = '+!+[]' + output; + } + if (number > 1) { + output = output.substr(1); + } + + MAPPING[number] = '[' + output + ']'; + } + } + + function replaceMap() { + let character = ''; let value; let i; let key; + + function replace(pattern, replacement) { + value = value.replace( + new RegExp(pattern, 'gi'), + replacement, + ); + } + + function digitReplacer(_, x) { + return MAPPING[x]; + } + + function numberReplacer(_, y) { + const values = y.split(''); + const head = +(values.shift()); + let output = '+[]'; + + if (head > 0) { + output = '+!' + output; + } + for (i = 1; i < head; i++) { + output = '+!+[]' + output; + } + if (head > 1) { + output = output.substr(1); + } + + return [output].concat(values).join('+').replace(/(\d)/g, digitReplacer); + } + + for (i = MIN; i <= MAX; i++) { + character = String.fromCharCode(i); + value = MAPPING[character]; + if (!value) { + continue; + } + + for (key in CONSTRUCTORS) { + replace('\\b' + key, CONSTRUCTORS[key] + '["constructor"]'); + } + + for (key in SIMPLE) { + replace(key, SIMPLE[key]); + } + + replace('(\\d\\d+)', numberReplacer); + replace('\\((\\d)\\)', digitReplacer); + replace('\\[(\\d)\\]', digitReplacer); + + replace('GLOBAL', GLOBAL); + replace('\\+""', '+[]'); + replace('""', '[]+[]'); + + MAPPING[character] = value; + } + } + + function replaceStrings() { + const regEx = /[^\[\]\(\)\!\+]{1}/g; + let all; let value; let missing; + let count = MAX - MIN; + + function findMissing() { + let all; let value; let done = false; + + missing = {}; + + for (all in MAPPING) { + value = MAPPING[all]; + + if (value && value.match(regEx)) { + missing[all] = value; + done = true; + } + } + + return done; + } + + function mappingReplacer(a, b) { + return b.split('').join('+'); + } + + function valueReplacer(c) { + return missing[c] ? c : MAPPING[c]; + } + + for (all in MAPPING) { + if (MAPPING[all]) { + MAPPING[all] = MAPPING[all].replace(/\"([^\"]+)\"/gi, mappingReplacer); + } + } + + while (findMissing()) { + for (all in missing) { + value = MAPPING[all]; + value = value.replace(regEx, valueReplacer); + + MAPPING[all] = value; + missing[all] = value; + } + + if (count-- === 0) { + console.error('Could not compile the following chars:', missing); + } + } + } + + function escapeSequence(c) { + const cc = c.charCodeAt(0); + if (cc < 256) { + return '\\' + cc.toString(8); + } else { + const cc16 = cc.toString(16); + return '\\u' + ('0000' + cc16).substring(cc16.length); + } + } + + function escapeSequenceForReplace(c) { + return escapeSequence(c).replace('\\', 't'); + } + + function encode(input, wrapWithEval, runInParentScope) { + let output = []; + + if (!input) { + return ''; + } + + let unmappped = ''; + for (const k in MAPPING) { + if (MAPPING[k]) { + unmappped += k; + } + } + unmappped = unmappped.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + unmappped = new RegExp('[^' + unmappped + ']', 'g'); + const unmappedCharactersCount = (input.match(unmappped) || []).length; + if (unmappedCharactersCount > 1) { + // Without this optimization one unmapped caracter has encoded length + // of about 3600 characters. Every additional unmapped character adds + // 2000 to the total length. For example, the lenght of `~` is 3605, + // `~~` is 5600, and `~~~` is 7595. + // + // The loader with replace has encoded length of about 5300 characters + // and every additional character adds 100 to the total length. + // In the same example the length of `~~` becomes 5371 and `~~~` -- 5463. + // + // So, when we have more than one unmapped character we want to encode whole input + // except select characters (that have encoded length less than about 70) + // into an escape sequence. + // + // NOTE: `t` should be escaped! + input = input.replace(/[^0123456789.adefilnrsuN]/g, escapeSequenceForReplace); + } else if (unmappedCharactersCount > 0) { + // Because we will wrap the input into a string we need to escape Backslash + // and Double quote characters (we do not need to worry about other characters + // because they are not mapped explicitly). + // The JSFuck-encoded representation of `\` is 2121 symbols, + // so esacped `\` is 4243 symbols and escaped `"` is 2261 symbols + // however the escape sequence of that characters are + // 2168 and 2155 symbols respectively, so it's more practical to + // rewrite them as escape sequences. + input = input.replace(/["\\]/g, escapeSequence); + // Convert all unmapped characters to escape sequence + input = input.replace(unmappped, escapeSequence); + } + + let r = ''; + for (const i in SIMPLE) { + r += i + '|'; + } + r+= '.'; + + input.replace(new RegExp(r, 'g'), function(c) { + let replacement = SIMPLE[c]; + if (replacement) { + output.push('(' + replacement + '+[])'); + } else { + replacement = MAPPING[c]; + if (replacement) { + output.push(replacement); + } else { + throw new Error('Found unmapped character: ' + c); + } + } + }); + + output = output.join('+'); + + if (/^\d$/.test(input)) { + output += '+[]'; + } + + if (unmappedCharactersCount > 1) { + // replace `t` with `\\` + output = '(' + output + ')[' + encode('split') + '](' + encode('t') + ')[' + encode('join') +'](' + encode('\\') + ')'; + } + + if (unmappedCharactersCount > 0) { + output = '[][' + encode('flat') + ']'+ + '[' + encode('constructor') + ']' + + '(' + encode('return"') + '+' + output + '+' + encode('"') + ')()'; + } + + if (wrapWithEval) { + if (runInParentScope) { + output = '[][' + encode('flat') + ']' + + '[' + encode('constructor') + ']' + + '(' + encode('return eval') + ')()' + + '(' + output + ')'; + } else { + output = '[][' + encode('flat') + ']' + + '[' + encode('constructor') + ']' + + '(' + output + ')()'; + } + } + + return output; + } + + fillMissingDigits(); + replaceMap(); + replaceStrings(); + + self.JSFuck = { + encode: encode, + }; +})(typeof(exports) === 'undefined' ? window : exports); diff --git a/src/three.ts b/src/three.ts index cd8c225..8ae2769 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit cd8c225742dbc275403ae272a61ab1d162f36c7a +Subproject commit 8ae2769518566e5662f2eefedb5415ddc2c9462a diff --git a/src/three/shader/loader/index.js b/src/three/shader/loader/index.js index 66e20b6..a0b5c77 100644 --- a/src/three/shader/loader/index.js +++ b/src/three/shader/loader/index.js @@ -1,6 +1,8 @@ const fs = require('fs'); const path = require('path'); +const glslMin = require('@yushijinhun/three-minifier-common/glsl-minifier'); + /** * * @param loader @@ -68,11 +70,11 @@ function processImports(loader, source, context, imports, cb) { } /** - * @todo find fix for #define and #if etc - * Remove comments, newlines, tabstops & unnecessary spaces - * @param src + * @param {string} src + * @return {string} */ function optimize(src) { + /* // src = src.replaceAll(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1'); const customs = ['+', '-', '/', '*', '=', `==`, '!=', '>=', '<=', '===', '>', '<', ',', '+=', '-=', '*=', '/=', '(', ')', '{', '}']; let spc = src.replaceAll(' ', ' '); @@ -89,11 +91,12 @@ function optimize(src) { spc = spc.replaceAll(/[\r\n]/, ''); } return spc.replaceAll(/[\t]/, ''); + */ + return glslMin.minifyGLSL(src); } /** - * - * @param source + * @param {string} source file */ exports.default = function(source) { this.cacheable(); diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 88a2df6..68604fd 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -193,10 +193,8 @@ export class WEAS extends CComponent { this.weasModule = mod; if (mod.memoryBuffer) { - setInterval(() => { - console.log('Yeah Boiii, we got shared memory access to WebAssembly worker!'); - console.log(mod.memoryBuffer); - }, 10000); + // @todo + // console.log('Yeah Boiii, we got shared memory access to WebAssembly worker!'); } this.updateSettings().then(() => { From 835d13ab790a4ce7452ff9ae96566fdb2f79b34d Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 23 Oct 2021 10:19:27 +0200 Subject: [PATCH 45/76] working preparation for three.ts --- src/wasc-worker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasc-worker b/src/wasc-worker index 51ab435..3881e54 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 51ab43515cfcd5412b0425ea8022b4c036c75658 +Subproject commit 3881e54818b16e8e8fce2662f3864a5abc3b080d From dd0cb1c86b3a3962d95d88604a806f944854f45f Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 23 Oct 2021 15:59:24 +0200 Subject: [PATCH 46/76] remove submodule better-docs & re-add package --- jsdoc.json | 4 ++-- src/wasc-worker | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jsdoc.json b/jsdoc.json index 5af53e2..986d0e6 100644 --- a/jsdoc.json +++ b/jsdoc.json @@ -1,6 +1,6 @@ { "opts": { - "template": "./src/wasc-worker/better-docs", + "template": "./src/wasc-worker/node_modules/better-docs", "encoding": "utf8", "destination": "./docs/", "recurse": true, @@ -8,7 +8,7 @@ "access": "all" }, "plugins": [ - "./src/wasc-worker/better-docs/typescript" + "./src/wasc-worker/node_modules/better-docs/typescript" ], "recurseDepth": 10, "source": { diff --git a/src/wasc-worker b/src/wasc-worker index 3881e54..e8c5719 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 3881e54818b16e8e8fce2662f3864a5abc3b080d +Subproject commit e8c5719b9c23f6bc53bf23f1c1f303cf0d52df03 From 16d738df62aeb2c5c1c716c27157c76a7453610e Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Fri, 24 Dec 2021 23:04:52 +0100 Subject: [PATCH 47/76] import & code refactoring --- index.ts | 27 - src/CComponent.ts | 9 +- src/CSettings.ts | 2 +- src/FPSta.ts | 8 +- src/LoadHelper.ts | 120 +++ src/ReloadHelper.ts | 35 +- src/Smallog.ts | 22 +- src/Util.ts | 6 + src/WEICUE.ts | 7 +- src/WEWA.ts | 896 ++++++++++--------- src/WarnHelper.ts | 4 +- src/{three => }/XRHelper.ts | 18 +- src/XRWebGL.d.ts | 299 +++++++ src/gles.d.ts | 802 +++++++++++++++++ src/{three/shader/loader => }/glsl.d.ts | 0 src/index.ts | 28 + src/offline/Offline.ts | 231 ++--- src/offline/OfflineHelper.ts | 136 +-- src/offline/OfflinePlugin.js | 273 +++--- src/renamer/RenamerPlugin.js | 110 ++- src/three.ts | 2 +- src/three/EffectComposer.ts | 54 +- src/three/index.ts | 1 - src/three/pass/BasePass.ts | 7 +- src/three/pass/FullScreenHelper.ts | 4 +- src/three/pass/RenderPass.ts | 122 ++- src/three/pass/ShaderPass.ts | 114 ++- src/three/pass/UnrealBloomPass.ts | 401 +++++---- src/three/shader/BlendShader.ts | 6 +- src/three/shader/BlurShader.ts | 11 +- src/three/shader/ChromaticShader.ts | 8 +- src/three/shader/CopyShader.ts | 6 +- src/three/shader/FXAAShader.ts | 9 +- src/three/shader/FractalMirrorShader.ts | 11 +- src/three/shader/LUTShader.ts | 6 +- src/three/shader/LuminosityHighPassShader.ts | 13 +- src/wasc-worker | 2 +- src/weas/Bea.ts | 18 +- src/weas/WEAS.ts | 81 +- 39 files changed, 2707 insertions(+), 1202 deletions(-) delete mode 100644 index.ts create mode 100644 src/LoadHelper.ts rename src/{three => }/XRHelper.ts (91%) create mode 100644 src/XRWebGL.d.ts create mode 100644 src/gles.d.ts rename src/{three/shader/loader => }/glsl.d.ts (100%) create mode 100644 src/index.ts diff --git a/index.ts b/index.ts deleted file mode 100644 index dfae3e1..0000000 --- a/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ - -export * from './src/CComponent'; -export * from './src/CSettings'; - -export * from './src/FPSta'; -export * from './src/ReloadHelper'; -export * from './src/Util'; -export * from './src/WarnHelper'; -export * from './src/weas'; -export * from './src/WEICUE'; -export * from './src/WEWA'; - -export * from './src/Smallog'; - -export * from './src/offline/OfflineHelper'; - -export * from './src/three'; - -export * from './src/wasc-worker'; diff --git a/src/CComponent.ts b/src/CComponent.ts index 2efc61c..eeb81b2 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -7,8 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import {CSettings} from './CSettings'; -import {Smallog} from './Smallog'; +import {CSettings, Smallog} from './'; /** * Base-Component for a TypeScript Web Wallpaper @@ -19,7 +18,7 @@ export class CComponent { /** * Important: Append your child objects, for settings to be applied correctly! */ - _internal_children: CComponent[] = []; + children: CComponent[] = []; /** * main Settings, need to be overwritten with Specific settings @@ -43,7 +42,7 @@ export class CComponent { this.needsUpdate = true; Smallog.debug(`ApplySetting: ${key}:${value}`); } - this._internal_children.forEach((ch) => found = ch.applySetting(key, value) || found); + this.children.forEach((ch) => found = ch.applySetting(key, value) || found); return found; } @@ -55,7 +54,7 @@ export class CComponent { * @public */ public updateAll(): void { - this._internal_children.forEach((c) => c.updateAll()); + this.children.forEach((c) => c.updateAll()); if (this.needsUpdate) this.updateSettings(); this.needsUpdate = false; } diff --git a/src/CSettings.ts b/src/CSettings.ts index 1f148dc..9fd2543 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -7,7 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import {Smallog} from './Smallog'; +import {Smallog} from './'; /** * Base-Component Settings helper diff --git a/src/FPSta.ts b/src/FPSta.ts index 8840328..911ac75 100644 --- a/src/FPSta.ts +++ b/src/FPSta.ts @@ -7,10 +7,8 @@ * See LICENSE file in the project root for full license information. */ -import {CComponent} from './CComponent'; -import {CSettings} from './CSettings'; -import {waitReady} from './Util'; -import {WEAS} from './weas'; +import {CComponent, CSettings, waitReady, WEAS} from '.'; + const ELM_ID = 'fpstats'; @@ -20,7 +18,7 @@ const ELM_ID = 'fpstats'; * @extends {CSettings} */ export class FPSettings extends CSettings { - debugging : boolean = false; + debugging: boolean = false; } /** diff --git a/src/LoadHelper.ts b/src/LoadHelper.ts new file mode 100644 index 0000000..dee93ea --- /dev/null +++ b/src/LoadHelper.ts @@ -0,0 +1,120 @@ +/** +* @author hexxone / https://hexx.one +* +* @license +* Copyright (c) 2021 hexxone All rights reserved. +* Licensed under the GNU GENERAL PUBLIC LICENSE. +* See LICENSE file in the project root for full license information. +* +* @description +* Displays a html load bar with given progress. +* +*/ + +import {waitReady} from '.'; + +const mainColor = '#69dc00aa'; + +/** +* Visual Reload-Bar +*/ +export class LoadHelper { + private _outer: HTMLDivElement; + private _bar: HTMLDivElement; + private _tex: HTMLDivElement; + + public progress = 0; + + /** + * Create and prepare when document is ready + */ + constructor() { + waitReady().then(() => { + this.injectCSS(); + this.injectHTML(); + this.setText(); + this.setProgress(1); + }); + } + + /** + * Make custom style + * @ignore + */ + private injectCSS() { + const st = document.createElement('style'); + st.innerHTML = ` + #ldhlpr-bar { + position: absolute; + opacity: 0; + top: 0px; + height: 10px; + width: 0%; + background-color: ${mainColor}; + transition: all 0.1s; + } + #ldhlpr-bar.show { + opacity: 1; + } + #ldhlpr-text { + position: absolute; + top: -30em; + width: 100%; + text-align: center; + text-shadow: 0 0 20px rgba(42, 255, 69, .5), 0 0 15px rgba(42, 255, 69, .5); + font-weight: 100; + font-size: 3em; + color: ${mainColor}; + transition: all 0.1s ease; + } + #ldhlpr-text.show { + top: 10px; + } + `; + document.head.append(st); + } + + /** + * Make custom html elements + * @ignore + */ + private injectHTML() { + this._outer = document.createElement('div'); + this._outer.id = 'loadhelper'; + this._bar = document.createElement('div'); + this._bar.id = 'ldhlpr-bar'; + this._tex = document.createElement('h1'); + this._tex.id = 'ldhlpr-text'; + this._outer.append(this._bar, this._tex); + document.body.append(this._outer); + } + + public setText(text = '') { + let txt = '--- Loading ---'; + if (text != '') txt += `
      ${text}`; + this._tex.innerHTML = txt; + } + + public setProgress(progress: number) { + this.progress = Math.max(0, Math.min(100, progress)); + this._bar.style.width = `${this.progress}%`; + } + + /** + * always reset bar to 0 + * @public + * @param {boolean} visible + */ + public show(visible: boolean) { + const e1 = this._bar; + const e2 = this._tex; + e1.classList.remove('show'); + e2.classList.remove('show'); + if (visible) { + setTimeout(() => { + e1.classList.add('show'); + e2.classList.add('show'); + }, 10); + } + } +}; diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index 4f2ea94..35e9748 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -11,9 +11,8 @@ * */ -import {CComponent} from './CComponent'; -import {CSettings} from './CSettings'; -import {waitReady} from './Util'; +import {CComponent, CSettings, waitReady} from './'; + /** * Reload-bar settings @@ -26,6 +25,10 @@ class ReloadSettings extends CSettings { * Visual Reload-Bar */ export class ReloadHelper extends CComponent { + private _outer: HTMLDivElement; + private _bar: HTMLDivElement; + private _tex: HTMLDivElement; + /** * @public */ @@ -39,6 +42,7 @@ export class ReloadHelper extends CComponent { waitReady().then(() => { this.injectCSS(); this.injectHTML(); + this.setText('Reloading...'); }); } @@ -97,15 +101,18 @@ export class ReloadHelper extends CComponent { * @ignore */ private injectHTML() { - const outer = document.createElement('div'); - outer.id = 'reloadhelper'; - const bar = document.createElement('div'); - bar.id = 'reload-bar'; - const tex = document.createElement('h1'); - tex.id = 'reload-text'; - tex.innerHTML = 'Reload'; - outer.append(bar, tex); - document.body.append(outer); + this._outer = document.createElement('div'); + this._outer.id = 'reloadhelper'; + this._bar = document.createElement('div'); + this._bar.id = 'reload-bar'; + this._tex = document.createElement('h1'); + this._tex.id = 'reload-text'; + this._outer.append(this._bar, this._tex); + document.body.append(this._outer); + } + + public setText(text: string) { + this._tex.innerHTML = text; } /** @@ -114,8 +121,8 @@ export class ReloadHelper extends CComponent { * @param {boolean} visible */ public show(visible: boolean) { - const e1 = document.getElementById('reload-bar'); - const e2 = document.getElementById('reload-text'); + const e1 = this._bar; + const e2 = this._tex; e1.classList.remove('show'); e2.classList.remove('show'); if (visible) { diff --git a/src/Smallog.ts b/src/Smallog.ts index 250e51d..b18229a 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -31,23 +31,22 @@ export enum LogLevel { /** * Small logging util, with name/time prefix & log levels -* @public */ class Smalog { - logLevel: LogLevel = LogLevel.Debug; - preFix: string = '[Smallog] '; - printTime: boolean = false; + logLevel: LogLevel = LogLevel.Debug; // @todo change default logging level + preFix = '[Smallog] '; + printTime = false; /** * trace exception calls * @param {string} def error message * @param {number} depth which call to pick - * @return {string} + * @return {string} error message * @ignore */ - traceCall(def: string, depth: number = 3) { + traceCall(def: string, depth = 3): string { try { - throw new Error('TraceCall()'); + throw new Error('trace()'); } catch (e) { // Examine e.stack here if (e.stack) { @@ -70,7 +69,8 @@ class Smalog { /** * set logging prefix - * @param {string} pre + * @param {string} pre new prefix + * @return {void} nothing */ setPrefix(pre: string) { this.preFix = pre; @@ -78,7 +78,8 @@ class Smalog { /** * set time prefix - * @param {boolean} print + * @param {boolean} print true to print time + * @return {void} nothing */ setPrintTime(print: boolean) { this.printTime = print; @@ -88,6 +89,7 @@ class Smalog { * print error message * @param {string} msg log * @param {string} hdr overwrite header + * @return {void} nothing */ error(msg: string, hdr: string = this.preFix) { if (this.printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; @@ -98,6 +100,7 @@ class Smalog { * print info message * @param {string} msg log * @param {string} hdr overwrite header + * @return {void} nothing */ info(msg: string, hdr: string = this.preFix) { if (this.logLevel >= 1) { @@ -111,6 +114,7 @@ class Smalog { * print debug message * @param {string} msg log * @param {string} hdr overwrite header + * @return {void} nothing */ debug(msg: string, hdr: string = this.preFix) { if (this.logLevel >= 2) { diff --git a/src/Util.ts b/src/Util.ts index bfee9e1..2ed4739 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -85,3 +85,9 @@ export function rgbToHSL(r_g_b: string): {h: number, s:number, l:number} { hsl.h /= 6; return hsl; } + + +/** + * Shorthand for Typed array stuff + */ +export const AnyTypedArray: Uint32Array | Float32Array | Float64Array | Uint8Array | Int8Array | Uint16Array | Int16Array | Int32Array | BigUint64Array | BigInt64Array = null; diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 1a140ef..07ed1cd 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -7,13 +7,10 @@ * See LICENSE file in the project root for full license information. */ -import {CComponent} from './CComponent'; -import {CSettings} from './CSettings'; -import {rgbToObj, waitReady} from './Util'; -import {Smallog} from './Smallog'; -import {WEAS} from './weas'; +import {CComponent, CSettings, rgbToObj, Smallog, waitReady, WEAS} from '.'; import {ICUE} from './ICUE'; + const IMG_SRC = './img/icue.png'; const ClassName: string = '[WEICUE] '; diff --git a/src/WEWA.ts b/src/WEWA.ts index 026c6e4..7b3927f 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -1,77 +1,74 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ - -import {waitReady} from './Util'; -import {Smallog} from './Smallog'; -import {OfflineHelper} from './offline/OfflineHelper'; -import {WascUtil} from './wasc-worker/WascUtil'; - -const LogHead = '[WEWWA] '; -const DefLang = 'de-de'; -const wral = 'wallpaperRegisterAudioListener'; -const proj = 'project.json'; + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ + +import { Smallog, waitReady, OfflineHelper, WascUtil } from "./"; + +const LogHead = "[WEWWA] "; +const DefLang = "de-de"; +const wral = "wallpaperRegisterAudioListener"; +const proj = "project.json"; /** -* WEWWA -*
      -* Wallpaper Engine Web Wallpaper Adapter -*
      -* This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine -* Web-Wallpaper project - so you can test, run & configure it from a normal web browser. -*
      -* REQUIREMENTS: -*
      -* - HTML5 Browser -*
      -* - the "project.json" needs to be in the root folder like "index.html" -*
      -* - this file needs to be included/built in your "index.html" -*
      -*
      -* FEATURES: -*
      -* - automatically detecting if the web wallpaper is opened by wallpaper engine or browser -*
      -* - if opened by wallpaper engine, nothing will happen -*
      -* - if opened by a browser: -*
      -* - use a ServiceWorker to make page always available offline -*
      -* - automatically load the "project.json" -*
      -* - parse the settings, languages & conditions -*
      -* - add respective html elements for each setting type & condition -*
      -* - put these elements into an option menu which can be hidden -*
      -* - check localStorage for already saved/customized values -*
      -* - apply all settings once -*
      -* - react to changes made in the ui and update them in the wallpaper -*
      -* - save changes made in the ui to localStorage -* -* -* @todo -* - inject "audio processing" setting -* -* lighthouse: -* - image explicit width/height -* - cf longer cache policy (2d?) -* - + * Wallpaper Engine Web Wallpaper Adapter + *
      + * This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine + * Web-Wallpaper project - so you can test, run & configure it from a normal web browser. + *
      + * REQUIREMENTS: + *
      + * - HTML5 Browser + *
      + * - the "project.json" needs to be in the root folder like "index.html" + *
      + * - this file needs to be included/built in your "index.html" + *
      + *
      + * FEATURES: + *
      + * - automatically detecting if the web wallpaper is opened by wallpaper engine or browser + *
      + * - if opened by wallpaper engine, nothing will happen + *
      + * - if opened by a browser: + *
      + * - use a ServiceWorker to make page always available offline + *
      + * - automatically load the "project.json" + *
      + * - parse the settings, languages & conditions + *
      + * - add respective html elements for each setting type & condition + *
      + * - put these elements into an option menu which can be hidden + *
      + * - check localStorage for already saved/customized values + *
      + * - apply all settings once + *
      + * - react to changes made in the ui and update them in the wallpaper + *
      + * - save changes made in the ui to localStorage + * + * + * @todo + * - inject "audio processing" setting + * + * lighthouse: + * - image explicit width/height + * - cf longer cache policy (2d?) + * - iniitialize, else => do nothing - * @param {Function} finished Callback for initializing the wallpaper - */ + * Check if we are running in Web-Mode + * if yes => iniitialize, else => do nothing + * @param {Function} finished Callback for initializing the wallpaper + */ constructor(finished) { if (window[wral]) { - Smallog.info('detected wallpaper engine => Standby.', LogHead); + Smallog.info("detected wallpaper engine => Standby.", LogHead); finished(); return; } - Smallog.info('wallpaper engine not detected => Init!', LogHead); + Smallog.info("wallpaper engine not detected => Init!", LogHead); // define audio listener first, so we dont miss when it gets registered. window[wral] = (callback) => { // set callback to be called later with analysed audio data this.audioCallback = callback; - Smallog.info('Registered wallpaper AudioListener.', LogHead); + Smallog.info("Registered wallpaper AudioListener.", LogHead); }; // intialize when ready waitReady().then(() => { // make the website available offline using service worker - OfflineHelper.register(document.title.replace(' ', '')).then(() => { + OfflineHelper.register(document.title.replace(" ", "")).then(() => { // continue initializing finished(); this.init(); @@ -126,13 +123,16 @@ export class WEWWA { } /** - * Initialize the Web Adapter - * @ignore - */ + * Initialize the Web Adapter + * @ignore + */ private init() { - WascUtil.myFetch(proj, 'json').then((proj) => { - if (proj.type != 'web') { - Smallog.error(`Error! Loaded ${proj} is not a web Wallpaper. How did this happen? Aborting...`, LogHead); + WascUtil.myFetch(proj, "json").then((proj) => { + if (proj.type != "web") { + Smallog.error( + `Error! Loaded ${proj} is not a web Wallpaper. How did this happen? Aborting...`, + LogHead + ); return; } @@ -141,36 +141,36 @@ export class WEWWA { this.project = proj; this.loadStorage(); this.addStyle(); - this.addMenu(localStorage.getItem('wewwaLang')); + this.addMenu(localStorage.getItem("wewwaLang")); this.evaluateSettings(); this.applyProp(proj.general.properties); }); } /** - * Load last settings from localStorage - * @ignore - */ + * Load last settings from localStorage + * @ignore + */ private loadStorage() { const props = this.project.general.properties; - const last = localStorage.getItem('wewwaLastProps'); + const last = localStorage.getItem("wewwaLastProps"); if (last != null) { const merged = Object.assign(props, JSON.parse(last)); merged.audioprocessing = { value: this.project.general.supportsaudioprocessing, - type: 'hidden', + type: "hidden", }; this.project.general.properties = merged; - Smallog.debug('Loaded & merged settings.', LogHead); + Smallog.debug("Loaded & merged settings.", LogHead); } } /** - * CSS Insertion - * @ignore - */ + * CSS Insertion + * @ignore + */ private addStyle() { - const st = document.createElement('style'); + const st = document.createElement("style"); // precalculation const minWidthPx = 420; const percentageWidth = 20; @@ -273,7 +273,9 @@ export class WEWWA { } #wewwaMenu.open, #wewwaIcon.open { - transform: translateX(min(-${percentageWidth * 1.1}vw, -${Math.floor(minWidthPx * 1.1)}px)); + transform: translateX(min(-${percentageWidth * 1.1}vw, -${Math.floor( + minWidthPx * 1.1 +)}px)); transition: transform 500ms ease; } @@ -296,10 +298,10 @@ export class WEWWA { } /** - * HTML Creation - * @param {string} lang WE language - * @ignore - */ + * HTML Creation + * @param {string} lang WE language + * @ignore + */ private addMenu(lang) { const self = this; if (this.htmlMenu) { @@ -316,8 +318,8 @@ export class WEWWA { const props = proj.general.properties; // create root menu - this.htmlMenu = ce('div'); - this.htmlMenu.id = 'wewwaMenu'; + this.htmlMenu = ce("div"); + this.htmlMenu.id = "wewwaMenu"; // create preview img wrap this.addMenuHeader(ce, proj); @@ -333,47 +335,55 @@ export class WEWWA { } /** - * Adds the Menu Icon - * @param {Function} ce CreateElement - * @param {Element} menu - * @ignore - */ + * Adds the Menu Icon + * @param {Function} ce CreateElement + * @param {Element} menu + * @ignore + */ private addMenuIcon(ce: (e: any) => any, menu = this.htmlMenu) { - const icon = this.htmlIcon = ce('div'); - icon.id = 'wewwaIcon'; - icon.addEventListener('click', () => { - if (this.htmlMenu.classList.contains('open')) { - this.htmlMenu.classList.remove('open'); + const icon = (this.htmlIcon = ce("div")); + icon.id = "wewwaIcon"; + icon.addEventListener("click", () => { + if (this.htmlMenu.classList.contains("open")) { + this.htmlMenu.classList.remove("open"); } else { - this.htmlMenu.classList.add('open'); + this.htmlMenu.classList.add("open"); } - if (icon.classList.contains('open')) { - icon.classList.remove('open'); + if (icon.classList.contains("open")) { + icon.classList.remove("open"); } else { - icon.classList.add('open'); + icon.classList.add("open"); } }); - const bar1 = ce('div'); - const bar2 = ce('div'); - const bar3 = ce('div'); + const bar1 = ce("div"); + const bar2 = ce("div"); + const bar3 = ce("div"); icon.append(bar1, bar2, bar3); document.body.append(icon); } /** - * Adds the actual Wallpaper Props as HTML - * @param {Function} ce Create Element wrapper - * @param {Object} proj project - * @param {object} self this - * @param {string} lang - * @param {object} props - * @param {Element} menu - * @ignore - */ - private addMenuSettings(ce: (e: any) => any, proj: any, self: this, lang: string, props: any, menu = this.htmlMenu) { - const tbl = ce('table'); - tbl.innerHTML = ' '; - const tblBody = ce('tbody'); + * Adds the actual Wallpaper Props as HTML + * @param {Function} ce Create Element wrapper + * @param {Object} proj project + * @param {object} self this + * @param {string} lang + * @param {object} props + * @param {Element} menu + * @ignore + */ + private addMenuSettings( + ce: (e: any) => any, + proj: any, + self: this, + lang: string, + props: any, + menu = this.htmlMenu + ) { + const tbl = ce("table"); + tbl.innerHTML = + ' '; + const tblBody = ce("tbody"); tbl.append(tblBody); // if app supports audio, add input menu & handlers @@ -382,20 +392,20 @@ export class WEWWA { } // create actual settings wrapper - const settings = ce('tr'); - settings.innerHTML = '

      Settings


      '; + const settings = ce("tr"); + settings.innerHTML = "

      Settings


      "; tblBody.append(settings); // pause checkbox - const pauseRow = ce('tr'); - const pauseOne = ce('td'); - pauseOne.innerHTML = '

      Pause on Unfocus

      '; - const pauseTwo = ce('td'); - pauseTwo.setAttribute('colspan', '2'); - const pauseBox = ce('input'); - pauseBox.setAttribute('type', 'checkbox'); - pauseBox.setAttribute('checked', this.pauseOnUnfocus); - pauseBox.addEventListener('change', function(e) { + const pauseRow = ce("tr"); + const pauseOne = ce("td"); + pauseOne.innerHTML = "

      Pause on Unfocus

      "; + const pauseTwo = ce("td"); + pauseTwo.setAttribute("colspan", "2"); + const pauseBox = ce("input"); + pauseBox.setAttribute("type", "checkbox"); + pauseBox.setAttribute("checked", this.pauseOnUnfocus); + pauseBox.addEventListener("change", function (e) { // eslint-disable-next-line no-invalid-this self.pauseOnUnfocus = this.checked; // unpause if paused @@ -422,8 +432,8 @@ export class WEWWA { } // split content from actual settings - const splitr = ce('tr'); - splitr.innerHTML = '
      '; + const splitr = ce("tr"); + splitr.innerHTML = "
      "; tblBody.append(splitr); // sort settings by order @@ -444,13 +454,13 @@ export class WEWWA { } /** - * Add missing default localization strings - * @param {Object} local - * @ignore - */ + * Add missing default localization strings + * @param {Object} local + * @ignore + */ private mergeLocals(local: any) { const locDefs = { - 'ui_browse_properties_scheme_color': 'Scheme color', + ui_browse_properties_scheme_color: "Scheme color", }; for (const loc in local) { if (!local[loc]) continue; @@ -463,20 +473,24 @@ export class WEWWA { } /** - * Adds the Footer Link to the Menu - * @param {Function} ce create element - * @param {Element} menu - * @ignore - */ + * Adds the Footer Link to the Menu + * @param {Function} ce create element + * @param {Element} menu + * @ignore + */ private addMenuFooter(ce: (e: any) => any, menu = this.htmlMenu) { - const preFoot = ce('div'); - preFoot.innerHTML = '
      '; - - const rst = ce('a'); - rst.classList.add('red'); - rst.innerHTML = 'Reset ↩️'; - rst.addEventListener('click', (e) => { - if (!window.confirm('This action will clear ALL local data!\r\n\r\nAre you sure?')) { + const preFoot = ce("div"); + preFoot.innerHTML = "
      "; + + const rst = ce("a"); + rst.classList.add("red"); + rst.innerHTML = "Reset ↩️"; + rst.addEventListener("click", (e) => { + if ( + !window.confirm( + "This action will clear ALL local data!\r\n\r\nAre you sure?" + ) + ) { return; } OfflineHelper.reset().then(() => { @@ -487,7 +501,7 @@ export class WEWWA { preFoot.append(rst); // footer with ident - const footer = ce('div'); + const footer = ce("div"); footer.innerHTML = `

      @@ -504,28 +518,28 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Add Language Menu - * @ignore - */ + * Add Language Menu + * @ignore + */ private makeMenuLocalization(ce: (e: any) => any, lang, local, props) { const self = this; // add html struct - const row = ce('tr'); - const td1 = ce('td'); - td1.innerHTML = '

      🌍

      '; - const td2 = ce('td'); - const lan = ce('select'); + const row = ce("tr"); + const td1 = ce("td"); + td1.innerHTML = "

      🌍

      "; + const td2 = ce("td"); + const lan = ce("select"); // process all for (const loc in local) { if (!loc) continue; // build select option for this - const lcs = ce('option'); + const lcs = ce("option"); lcs.value = loc; lcs.innerHTML = loc.toUpperCase(); lan.append(lcs); // check for correct language code if (loc != lang) continue; - else lcs.setAttribute('selected', 'true'); + else lcs.setAttribute("selected", "true"); // set properties translated text for (const p in props) { if (!p) continue; @@ -534,7 +548,7 @@ export class WEWWA { const rTxt = local[loc][pTxt]; if (rTxt) itm.realText = rTxt; // process combo box values - if (itm.type == 'combo') { + if (itm.type == "combo") { for (const o of itm.options) { const lTxt = local[loc][o.label]; if (lTxt) o.realLabel = lTxt; @@ -543,15 +557,15 @@ export class WEWWA { } } // if changed, do it all over again. - lan.addEventListener('change', function(e) { + lan.addEventListener("change", function (e) { // eslint-disable-next-line no-invalid-this - localStorage.setItem('wewwaLang', this.value); + localStorage.setItem("wewwaLang", this.value); // eslint-disable-next-line no-invalid-this self.addMenu(this.value); self.evaluateSettings(); (self.htmlIcon as any).click(); }); - td2.setAttribute('colspan', '2'); + td2.setAttribute("colspan", "2"); td2.append(lan); row.append(td1, td2); return row; @@ -559,48 +573,51 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Add Audio Menu - * @ignore - */ + * Add Audio Menu + * @ignore + */ private addMenuAudio(ce: (e: any) => any, tblBody: any) { // audio input methods - const row = ce('tr'); + const row = ce("tr"); - const td1 = ce('td'); - td1.innerHTML = '

      Audio Input


      '; - td1.setAttribute('colspan', '3'); + const td1 = ce("td"); + td1.innerHTML = "

      Audio Input


      "; + td1.setAttribute("colspan", "3"); // Microphone input - const aBtn1 = ce('a'); - aBtn1.classList.add('audio'); - aBtn1.innerHTML = 'Microphone'; - aBtn1.addEventListener('click', (e) => { + const aBtn1 = ce("a"); + aBtn1.classList.add("audio"); + aBtn1.innerHTML = "Microphone"; + aBtn1.addEventListener("click", (e) => { this.initMicrophone(); }); // Desktop Audio input - const aBtn2 = ce('a'); - aBtn2.classList.add('audio'); - aBtn2.innerHTML = 'Desktop Audio (Chrome)'; - aBtn2.addEventListener('click', (e) => { + const aBtn2 = ce("a"); + aBtn2.classList.add("audio"); + aBtn2.innerHTML = "Desktop Audio (Chrome)"; + aBtn2.addEventListener("click", (e) => { this.initDesktop(); }); // File Url input - const aBtn3 = ce('a'); - aBtn3.classList.add('audio'); - aBtn3.innerHTML = 'Select URL'; - aBtn3.addEventListener('click', (e) => { - const uri = prompt('Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!', 'https://example.com/test.mp3'); + const aBtn3 = ce("a"); + aBtn3.classList.add("audio"); + aBtn3.innerHTML = "Select URL"; + aBtn3.addEventListener("click", (e) => { + const uri = prompt( + "Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!", + "https://example.com/test.mp3" + ); this.initFile(uri); }); // System file input - const aBtn4 = ce('input'); - aBtn4.id = 'wewwaAudioInput'; - aBtn4.innerHTML = 'Select File'; - aBtn4.setAttribute('type', 'file'); - aBtn4.addEventListener('change', (e) => { + const aBtn4 = ce("input"); + aBtn4.id = "wewwaAudioInput"; + aBtn4.innerHTML = "Select File"; + aBtn4.setAttribute("type", "file"); + aBtn4.addEventListener("change", (e) => { const file = (e.target as any).files[0]; if (!file) { return; @@ -611,44 +628,50 @@ export class WEWWA { td1.append(aBtn1, aBtn2, aBtn3, aBtn4); row.append(td1); - // file drag & drop area - const dropRow = ce('tr'); - const dropCol1 = ce('td'); - const dropCol2 = ce('td'); - dropCol1.setAttribute('colspan', '3'); - - const dropArea = ce('div'); - dropArea.innerHTML = 'Drag & Drop'; - dropArea.classList.add(...['droparea', 'audio']); - dropArea.addEventListener('dragover', (evt) => { - evt.stopPropagation(); - evt.preventDefault(); - evt.dataTransfer.dropEffect = 'copy'; - }, false); - dropArea.addEventListener('drop', (e) => { - e.stopPropagation(); - e.preventDefault(); - const droppedFiles = e.dataTransfer.files; - this.initFile(droppedFiles[0]); - }, false); + const dropRow = ce("tr"); + const dropCol1 = ce("td"); + const dropCol2 = ce("td"); + dropCol1.setAttribute("colspan", "3"); + + const dropArea = ce("div"); + dropArea.innerHTML = "Drag & Drop"; + dropArea.classList.add(...["droparea", "audio"]); + dropArea.addEventListener( + "dragover", + (evt) => { + evt.stopPropagation(); + evt.preventDefault(); + evt.dataTransfer.dropEffect = "copy"; + }, + false + ); + dropArea.addEventListener( + "drop", + (e) => { + e.stopPropagation(); + e.preventDefault(); + const droppedFiles = e.dataTransfer.files; + this.initFile(droppedFiles[0]); + }, + false + ); dropCol1.append(dropArea); dropRow.append(dropCol1, dropCol2); - // Play & Stop Btn - const hrrow = ce('tr'); - const hrtd1 = ce('td'); - hrtd1.id = 'audioMarker'; - hrtd1.setAttribute('colspan', '3'); - const stopBtn = ce('a'); - stopBtn.classList.add('red'); - stopBtn.innerHTML = 'Stop All Audio'; - stopBtn.addEventListener('click', (e) => { + const hrrow = ce("tr"); + const hrtd1 = ce("td"); + hrtd1.id = "audioMarker"; + hrtd1.setAttribute("colspan", "3"); + const stopBtn = ce("a"); + stopBtn.classList.add("red"); + stopBtn.innerHTML = "Stop All Audio"; + stopBtn.addEventListener("click", (e) => { this.stopAudioInterval(); }); hrtd1.append(stopBtn); - const hrtd2 = ce('td'); + const hrtd2 = ce("td"); hrrow.append(hrtd1, hrtd2); // finally add rows to table @@ -657,43 +680,51 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Add preview Image, Title and Link - * @ignore - */ - private addMenuHeader(ce: (e: any) => any, proj: any, menu = this.htmlMenu) { - const preview = ce('img'); - preview.setAttribute('src', proj.preview); - preview.setAttribute('alt', 'Steam Workshop Preview Image'); + * Add preview Image, Title and Link + * @ignore + */ + private addMenuHeader( + ce: (e: any) => any, + proj: any, + menu = this.htmlMenu + ) { + const preview = ce("img"); + preview.setAttribute("src", proj.preview); + preview.setAttribute("alt", "Steam Workshop Preview Image"); // create menu app title - const header = ce('div'); - header.innerHTML = '

      ' + proj.title + '

      '; + const header = ce("div"); + header.innerHTML = "

      " + proj.title + "

      "; // create workshop link - const link = ce('a'); - link.setAttribute('rel', 'noreferrer'); - link.setAttribute('href', 'https://steamcommunity.com/sharedfiles/filedetails/?id=' + proj.workshopid); - link.setAttribute('target', '_blank'); - link.innerHTML = '

      Open Workshop Page

      '; + const link = ce("a"); + link.setAttribute("rel", "noreferrer"); + link.setAttribute( + "href", + "https://steamcommunity.com/sharedfiles/filedetails/?id=" + + proj.workshopid + ); + link.setAttribute("target", "_blank"); + link.innerHTML = "

      Open Workshop Page

      "; menu.append(preview, header, link); } // eslint-disable-next-line valid-jsdoc /** - * Create an HTML Menu Item from project json property - * @ignore - */ + * Create an HTML Menu Item from project json property + * @ignore + */ private createItem(prop, itm) { - if (!itm.type || itm.type == 'hidden') return null; + if (!itm.type || itm.type == "hidden") return null; const self = this; const ce = (e) => document.createElement(e); // table structure - const row = ce('tr'); - row.setAttribute('id', 'wewwa_' + prop); + const row = ce("tr"); + row.setAttribute("id", "wewwa_" + prop); // Text - const column1 = ce('td'); - column1.classList.add('left'); + const column1 = ce("td"); + column1.classList.add("left"); // Input - const column2 = ce('td'); - column2.classList.add('right'); + const column2 = ce("td"); + column2.classList.add("right"); // optional NumericUpDown Column let column3 = null; // div or label text element @@ -704,50 +735,51 @@ export class WEWWA { // Process actual prop type switch (itm.type) { // only text across 3 columns - case 'text': - txt = ce('div'); + case "text": + txt = ce("div"); txt.innerHTML = itm.realText ? itm.realText : itm.text; - column1.setAttribute('colspan', 3); + column1.setAttribute("colspan", 3); break; // combo select-box across 2 columns - case 'combo': - inpt = ce('select'); + case "combo": + inpt = ce("select"); // set options for (const o of itm.options) { - const opt = ce('option'); - opt.setAttribute('value', o.value); + const opt = ce("option"); + opt.setAttribute("value", o.value); opt.innerText = o.realLabel ? o.realLabel : o.label; - if (itm.value == o.value) opt.setAttribute('selected', true); + if (itm.value == o.value) + opt.setAttribute("selected", true); inpt.appendChild(opt); } break; // system color picker across 2 columns - case 'color': - inpt = ce('input'); - inpt.setAttribute('type', 'color'); + case "color": + inpt = ce("input"); + inpt.setAttribute("type", "color"); break; // Checkbox across 2 columns - case 'bool': - inpt = ce('input'); - inpt.setAttribute('type', 'checkbox'); - inpt.setAttribute('readonly', true); + case "bool": + inpt = ce("input"); + inpt.setAttribute("type", "checkbox"); + inpt.setAttribute("readonly", true); break; // Slider input across 1 column; + 1 column Up/Down - case 'slider': + case "slider": { const canEdit = itm.editable; // create numeric-up-down - const sliderVal = ce(canEdit ? 'input' : 'output'); - sliderVal.name = 'wewwa_out_' + prop; - sliderVal.setAttribute('id', sliderVal.name); - sliderVal.setAttribute('type', 'number'); - sliderVal.style.width = '75%'; + const sliderVal = ce(canEdit ? "input" : "output"); + sliderVal.name = "wewwa_out_" + prop; + sliderVal.setAttribute("id", sliderVal.name); + sliderVal.setAttribute("type", "number"); + sliderVal.style.width = "75%"; if (canEdit) { - sliderVal.setAttribute('value', itm.value); - sliderVal.addEventListener('change', function(e) { + sliderVal.setAttribute("value", itm.value); + sliderVal.addEventListener("change", function (e) { // eslint-disable-next-line no-invalid-this self.setProperty(prop, this); }); @@ -755,48 +787,48 @@ export class WEWWA { sliderVal.innerHTML = itm.value; } // create td3 - column3 = ce('td'); + column3 = ce("td"); column3.append(sliderVal); // create actual slider & values - inpt = ce('input'); - inpt.setAttribute('type', 'range'); + inpt = ce("input"); + inpt.setAttribute("type", "range"); inpt.max = itm.max; inpt.min = itm.min; inpt.step = 0.1; break; - - // Text input across 2 columns - case 'textinput': - inpt = ce('input'); - inpt.setAttribute('type', 'text'); + } + // Text input across 2 columns + case "textinput": + inpt = ce("input"); + inpt.setAttribute("type", "text"); break; // File input across 2 columns - case 'file': - inpt = ce('input'); - inpt.setAttribute('type', 'file'); + case "file": + inpt = ce("input"); + inpt.setAttribute("type", "file"); break; default: - Smallog.error('unkown setting type: ' + itm.type, LogHead); + Smallog.error("unkown setting type: " + itm.type, LogHead); break; } - const eid = 'wewwa_prop_' + prop; + const eid = "wewwa_prop_" + prop; // make input label if not text if (!txt) { - txt = ce('label'); - txt.setAttribute('for', eid); + txt = ce("label"); + txt.setAttribute("for", eid); txt.innerHTML = itm.realText ? itm.realText : itm.text; } column1.append(txt); // listen for changes if input type (no text) if (inpt) { - inpt.style.width = '100%'; - inpt.setAttribute('id', eid); - inpt.addEventListener('change', function(e) { + inpt.style.width = "100%"; + inpt.setAttribute("id", eid); + inpt.addEventListener("change", function (e) { // eslint-disable-next-line no-invalid-this self.setProperty(prop, this); }); @@ -806,29 +838,28 @@ export class WEWWA { // append td3 or stretch td2? row.append(column1, column2); if (column3) row.append(column3); - else column2.setAttribute('colspan', 2); + else column2.setAttribute("colspan", 2); return row; } - // ------------------------------------- // Settings Helper // ------------------------------------- // eslint-disable-next-line valid-jsdoc /** - * Callback for UI-Settings changes - * Will apply them to the storage and running wallaper. - * @public - */ + * Callback for UI-Settings changes + * Will apply them to the storage and running wallaper. + * @public + */ public setProperty(prop, elm) { // get the type and apply the value const props = this.project.general.properties; // check for legit setting... if (!props[prop]) { - Smallog.error('SetProperty name not found: ' + prop, LogHead); + Smallog.error("SetProperty name not found: " + prop, LogHead); return; } @@ -845,42 +876,48 @@ export class WEWWA { // process value based on DOM element type switch (props[prop].type) { - case 'bool': + case "bool": applyCall(elm.checked == true); break; - case 'color': + case "color": applyCall(this.hexToRgb(elm.value)); break; - case 'file': + case "file": this.loadXHRSaveLocal(elm.value, (res) => applyCall(res)); break; - case 'slider': - if (elm.name.includes('_out_')) { - const inpt: any = document.querySelector('#wewwa_' + prop); + case "slider": + if (elm.name.includes("_out_")) { + const inpt: any = document.querySelector("#wewwa_" + prop); if (inpt) inpt.value = elm.value; - else Smallog.error('Slider not found: ' + prop, LogHead); + else Smallog.error("Slider not found: " + prop, LogHead); } else { - const slide: any = document.querySelector('#wewwa_out_' + prop); + const slide: any = document.querySelector( + "#wewwa_out_" + prop + ); if (slide) slide.value = elm.value; - else Smallog.error('Numericupdown not found: ' + prop, LogHead); + else + Smallog.error( + "Numericupdown not found: " + prop, + LogHead + ); } - case 'combo': - case 'textinput': + case "combo": + case "textinput": applyCall(elm.value); break; } } /** - * will load the given file and return it as dataURL. - * this way we can easily store whole files in the configuration & localStorage. - * its not safe that this works with something else than image files. - * @param {string} url - * @param {function (data: (string | ArrayBuffer)): void} resCall - * @ignore - */ + * will load the given file and return it as dataURL. + * this way we can easily store whole files in the configuration & localStorage. + * its not safe that this works with something else than image files. + * @param {string} url + * @param {function (data: (string | ArrayBuffer)): void} resCall + * @ignore + */ private loadXHRSaveLocal(url, resCall) { - WascUtil.myFetch(url, 'blob').then((resp) => { + WascUtil.myFetch(url, "blob").then((resp) => { // Read out file contents as a Data URL const fReader = new FileReader(); // onload needed since Google Chrome doesn't support addEventListener for FileReader @@ -891,15 +928,15 @@ export class WEWWA { } /** - * Show or hide menu items based on eval condition - * @public - */ + * Show or hide menu items based on eval condition + * @public + */ public evaluateSettings() { // dynamic prefix for evaluation - const pre = 'wewwaProps'; + const pre = "wewwaProps"; const wewwaProps = this.project.general.properties; - localStorage.setItem('wewwaLastProps', JSON.stringify(wewwaProps)); + localStorage.setItem("wewwaLastProps", JSON.stringify(wewwaProps)); for (const p in wewwaProps) { if (!p) continue; const prop = wewwaProps[p]; @@ -908,68 +945,76 @@ export class WEWWA { let visible = true; if (prop.condition != null) { // copy our condition string to modify - let cprop = String(prop.condition).split(' ').join(''); + let cprop = String(prop.condition).split(" ").join(""); // remove whitespaces and split to partials by logic operators const partials = cprop.split(/&&|\|\|/); // loop all partial values of the check for (const part of partials) { - let prefix = pre + '.'; + let prefix = pre + "."; const onlyVal = part.match(/[!a-zA-Z0-9_\.]*/)[0]; - if (!onlyVal.startsWith(prefix) && !onlyVal.startsWith('!' + prefix)) { + if ( + !onlyVal.startsWith(prefix) && + !onlyVal.startsWith("!" + prefix) + ) { // fix for inverted values let replW = onlyVal; - if (replW.startsWith('!')) { + if (replW.startsWith("!")) { replW = replW.substr(1); - prefix = '!' + prefix; + prefix = "!" + prefix; } // Smallog.Debug("replace: " + onlyVal + " >> " + prefix + replW); cprop = cprop.replace(onlyVal, prefix + replW); } } try { - visible = new Function(pre, 'return ('+cprop+')')(wewwaProps) === true; + visible = + new Function(pre, "return (" + cprop + ")")( + wewwaProps + ) === true; } catch (e) { - Smallog.error('Error: (' + cprop + ') for: ' + p + ' => ' + e, LogHead); + Smallog.error( + "Error: (" + cprop + ") for: " + p + " => " + e, + LogHead + ); } } // get input dom element - const htElm = document.getElementById('wewwa_' + p); + const htElm = document.getElementById("wewwa_" + p); if (!htElm || htElm.childNodes.length < 2) continue; - if (visible) htElm.classList.remove('hide'); - else htElm.classList.add('hide'); + if (visible) htElm.classList.remove("hide"); + else htElm.classList.add("hide"); // set its value const elm: any = htElm.childNodes[1].childNodes[0]; switch (prop.type) { - case 'color': + case "color": elm.value = this.rgbToHex(prop.value); break; - case 'bool': + case "bool": elm.checked = prop.value == true; break; - case 'slider': - case 'combo': - case 'textinput': + case "slider": + case "combo": + case "textinput": elm.value = prop.value; break; } } } - // ------------------------------------- // Wallpaper Interface // ------------------------------------- // eslint-disable-next-line valid-jsdoc /** - * Send one or more properties to the Wallpaper - * @public - */ + * Send one or more properties to the Wallpaper + * @public + */ public applyProp(prop) { - const wpl = window['wallpaperPropertyListener']; + const wpl = window["wallpaperPropertyListener"]; if (wpl && wpl.applyUserProperties) { wpl.applyUserProperties(prop); } @@ -977,11 +1022,11 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Send paused-status to the Wallpaper - * @public - */ + * Send paused-status to the Wallpaper + * @public + */ public setPaused(val: boolean) { - const wpl = window['wallpaperPropertyListener']; + const wpl = window["wallpaperPropertyListener"]; if (this.isPaused == val) return; if (val && !this.pauseOnUnfocus) return; if (wpl && wpl.setPaused) { @@ -990,7 +1035,6 @@ export class WEWWA { } } - // ------------------------------------- // UI Color Input conversion // ------------------------------------- @@ -1000,16 +1044,22 @@ export class WEWWA { // eslint-disable-next-line require-jsdoc function cth(c) { const h = Math.floor(c * 255).toString(16); - return h.length == 1 ? '0' + h : h; + return h.length == 1 ? "0" + h : h; } - const spl = rgb.split(' '); - return '#' + cth(spl[0]) + cth(spl[1]) + cth(spl[2]); + const spl = rgb.split(" "); + return "#" + cth(spl[0]) + cth(spl[1]) + cth(spl[2]); } // eslint-disable-next-line require-jsdoc private hexToRgb(hex) { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255].join(' ') : null; + return result + ? [ + parseInt(result[1], 16) / 255, + parseInt(result[2], 16) / 255, + parseInt(result[3], 16) / 255, + ].join(" ") + : null; } // ------------------------------------- @@ -1017,83 +1067,95 @@ export class WEWWA { // ------------------------------------- /** - * Request microphone from browser - * @ignore - */ + * Request microphone from browser + * @ignore + */ private initMicrophone() { const md = navigator.mediaDevices as any; - if (!md['getUserMedia']) return; + if (!md["getUserMedia"]) return; md.getUserMedia({ audio: true, - }).then((stream) => { - Smallog.debug('Got Microphone MediaStream!'); - // stop previous analyzer - this.stopAudioInterval(); - this.makeAnalyzer(stream); - }).catch((err) => { - Smallog.error(err, LogHead); - if (location.protocol != 'https:') { - const r = confirm('Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS! Press \'ok\'/\'yes\' to get redirected from HTTP => HTTPS.'); - if (r) window.location.href = window.location.href.replace('http', 'https'); - } - }); + }) + .then((stream) => { + Smallog.debug("Got Microphone MediaStream!"); + // stop previous analyzer + this.stopAudioInterval(); + this.makeAnalyzer(stream); + }) + .catch((err) => { + Smallog.error(err, LogHead); + if (location.protocol != "https:") { + const r = confirm( + "Activating the Microphone failed! Your Browser might require the site to be loaded using HTTPS! Press 'ok'/'yes' to get redirected from HTTP => HTTPS." + ); + if (r) + window.location.href = window.location.href.replace( + "http", + "https" + ); + } + }); } /** - * Initiate Desktop auddio streaming - */ + * Initiate Desktop auddio streaming + */ private async initDesktop() { const md = navigator.mediaDevices as any; - if (!md['getDisplayMedia']) return; + if (!md["getDisplayMedia"]) return; md.getDisplayMedia({ video: true, audio: true, - }).then((stream) => { - Smallog.debug('Got Desktop MediaStream!'); - // stop previous analyzer - this.stopAudioInterval(); - this.makeAnalyzer(stream); - }).catch((e) => { - Smallog.error('Cant Open Desktop Audio Stream!', '[WEWA] '); - console.error(e); - }); + }) + .then((stream) => { + Smallog.debug("Got Desktop MediaStream!"); + // stop previous analyzer + this.stopAudioInterval(); + this.makeAnalyzer(stream); + }) + .catch((e) => { + Smallog.error("Cant Open Desktop Audio Stream!", "[WEWA] "); + console.error(e); + }); } // eslint-disable-next-line valid-jsdoc /** - * Start the audio processing & analyzer - * @ignore - */ + * Start the audio processing & analyzer + * @ignore + */ private initFile(file) { // stop previous analyzer this.stopAudioInterval(); if (!file) return; // create player - this.audio = document.createElement('audio'); + this.audio = document.createElement("audio"); this.audio.src = file.name ? URL.createObjectURL(file) : file; this.audio.autoplay = true; - this.audio.setAttribute('controls', 'true'); + this.audio.setAttribute("controls", "true"); this.audio.play(); // insert before marker - const markr = document.getElementById('audioMarker'); + const markr = document.getElementById("audioMarker"); markr.prepend(this.audio); this.makeAnalyzer(this.audio); } /** - * - * @param {MediaStream} src - */ + * + * @param {MediaStream} src + */ private makeAnalyzer(src: MediaStream | HTMLAudioElement) { // new context - this.ctx = new (window.AudioContext || window['webkitAudioContext'])({sampleRate: 48000}); + this.ctx = new (window.AudioContext || window["webkitAudioContext"])({ + sampleRate: 48000, + }); // microphone or desktop stream sauce if (src instanceof MediaStream) { this.source = this.ctx.createMediaStreamSource(src); // hack for firefox to keep stream running - window['persistAudioStream'] = src; + window["persistAudioStream"] = src; } // audio html element sauce if (src instanceof HTMLAudioElement) { @@ -1112,16 +1174,16 @@ export class WEWWA { } /** - * Start the processing loop - * @ignore - */ + * Start the processing loop + * @ignore + */ private startAudioInterval() { const data = new Uint8Array(128); // 33ms ~~ 30fps this.audioInterval = window.setInterval(() => { if (this.audioCallback == null) { this.stopAudioInterval(); - Smallog.error('no AudioCallback!', LogHead); + Smallog.error("no AudioCallback!", LogHead); return; } this.analyser.getByteFrequencyData(data); @@ -1129,18 +1191,18 @@ export class WEWWA { this.audioCallback(stereo); }, 33); // tell Wallpaper we are sending audio - this.applyProp({audioprocessing: {value: true}}); + this.applyProp({ audioprocessing: { value: true } }); } // eslint-disable-next-line valid-jsdoc /** - * html5 audio analyser gives us mono data from 0(bass) to 128(treble) - * however, wallpaper engine expects stereo data in following format: - * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) - * so we do some array transformation... and divide by 255 (8bit-uint becomes float) - * @ignore - */ - private convertAudio(data) { + * html5 audio analyser gives us mono data from 0(bass) to 128(treble) + * however, wallpaper engine expects stereo data in following format: + * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) + * so we do some array transformation... and divide by 255 (8bit-uint becomes float) + * @ignore + */ + private convertAudio(data: Uint8Array) { const stereo = []; let sIdx = 0; for (let i = 0; i < 64; i++) { @@ -1151,12 +1213,12 @@ export class WEWWA { } /** - * Stop the processing loop - * @public - */ + * Stop the processing loop + * @public + */ public stopAudioInterval() { - window['persistAudioStream'] = null; - document.getElementById('wewwaAudioInput').setAttribute('value', ''); + window["persistAudioStream"] = null; + document.getElementById("wewwaAudioInput").setAttribute("value", ""); if (this.audio) { this.audio.remove(); } diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index a50cb68..82c035a 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -7,9 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import {CComponent} from './CComponent'; -import {CSettings} from './CSettings'; -import {waitReady} from './Util'; +import {CSettings, CComponent, waitReady} from './'; const ELM_ID = 'triggerwarn'; const IMG_SRC = './img/triggerwarn.png'; diff --git a/src/three/XRHelper.ts b/src/XRHelper.ts similarity index 91% rename from src/three/XRHelper.ts rename to src/XRHelper.ts index 37698cb..badda37 100644 --- a/src/three/XRHelper.ts +++ b/src/XRHelper.ts @@ -4,17 +4,16 @@ * @author Mugen87 / https://github.com/Mugen87 */ -import {Navigator, XRSession} from 'three'; -import {CComponent} from '../CComponent'; -import {CSettings} from '../CSettings'; -import {waitReady} from '../Util'; +import {CSettings, CComponent, waitReady} from './'; +import {XRSessionInit} from './XRWebGL'; + /** * XR Settings * @extends {CSettings} */ export class XRSettings extends CSettings { - xr_mode: boolean = false; + private xr_mode: boolean = false; } /** @@ -49,7 +48,7 @@ export class XRHelper extends CComponent { btn.disabled = true; btn.style.display = 'none'; btn.style.position = 'absolute'; - btn.style.bottom = '10px'; + btn.style.bottom = '50px'; btn.style.padding = '12px 6px'; btn.style.border = '1px solid #fff'; btn.style.borderRadius = '4px'; @@ -60,6 +59,9 @@ export class XRHelper extends CComponent { btn.style.opacity = '0.5'; btn.style.outline = 'none'; btn.style.zIndex = '99999'; + // auto center horizontally + btn.style.left = '50%'; + btn.style.transform = 'translate(-50%, 0)'; btn.onmouseenter = () => { btn.style.opacity = '1.0'; @@ -122,7 +124,9 @@ export class XRHelper extends CComponent { // requestReferenceSpace call will fail if it turns out to be unavailable. // ('local' is always available for immersive sessions and doesn't need to // be requested separately.) - const sessionInit = {optionalFeatures: ['local-floor']}; /* , 'bounded-floor'*/ + const sessionInit: XRSessionInit = { + optionalFeatures: ['local-floor'], + }; this.nav.xr.requestSession('immersive-vr', sessionInit).then((sess) => { this.currentSession = sess; diff --git a/src/XRWebGL.d.ts b/src/XRWebGL.d.ts new file mode 100644 index 0000000..9d55d68 --- /dev/null +++ b/src/XRWebGL.d.ts @@ -0,0 +1,299 @@ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ +/* eslint-disable no-unused-vars */ +// # General utilities + +type EventHandlerNonNull = (event: Event) => void; + +type EventHandler = EventHandlerNonNull | null | undefined; + +// DOMString is not exactly a string (https://heycam.github.io/webidl/#idl-DOMString). +// (TODO) +type DOMString = string; + +// # WebXR + +export type XRSessionMode = "inline" | "immersive-vr" | "immersive-ar"; + +export type XRReferenceSpaceType = + | "viewer" + | "local" + | "local-floor" + | "bounded-floor" + | "unbounded"; + +export type XRVisibilityState = "visible" | "visible-blurred" | "hidden"; + +export type XREye = "none" | "left" | "right"; + +export type XRHandedness = "none" | "left" | "right"; + +export type XRTargetRayMode = "gaze" | "tracked-pointer" | "screen"; + +export type XRWebGLRenderingContext = + | WebGLRenderingContext + | WebGL2RenderingContext; + +export interface XRSessionInit { + requiredFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) + optionalFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) +} + +declare global { + export class XRRigidTransform { + constructor(position?: DOMPointInit, orientation?: DOMPointInit); + readonly position: DOMPointReadOnly; + readonly orientation: DOMPointReadOnly; + readonly matrix: Float32Array; + readonly inverse: XRRigidTransform; + } + + export interface XRViewport { + readonly x: number; + readonly y: number; + readonly width: number; + readonly height: number; + } +} + +// ## Events + +export interface XRSessionEventInit extends EventInit { + session: XRSession; +} + +export interface XRInputSourceEventInit extends EventInit { + frame: XRFrame; + inputSource: XRInputSource; +} + +export interface XRInputSourcesChangeEventInit extends EventInit { + session: XRSession; + added: XRInputSource[]; // FrozenArray (TODO?) + removed: XRInputSource[]; // FrozenArray (TODO?) +} + +export interface XRReferenceSpaceEventInit extends EventInit { + referenceSpace: XRReferenceSpace; + transform?: XRRigidTransform | null; +} + +declare global { + export class XRSessionEvent extends Event { + constructor(type: DOMString, eventInitDict: XRSessionEventInit); + readonly session: XRSession; + } + + export class XRInputSourceEvent extends Event { + constructor(type: DOMString, eventInitDict: XRInputSourceEventInit); + readonly frame: XRFrame; + readonly inputSource: XRInputSource; + } + export class XRInputSourcesChangeEvent extends Event { + constructor( + type: DOMString, + eventInitDict: XRInputSourcesChangeEventInit + ); + readonly session: XRSession; + readonly added: XRInputSource[]; // FrozenArray (TODO?) + readonly removed: XRInputSource[]; // FrozenArray (TODO?) + } + + export class XRReferenceSpaceEvent extends Event { + constructor(type: DOMString, eventInitDict: XRReferenceSpaceEventInit); + readonly referenceSpace: XRReferenceSpace; + readonly transform: XRRigidTransform | null; + } +} + +// ## Permissions + +export interface XRPermissionDescriptor extends PermissionDescriptor { + mode?: XRSessionMode; + requiredFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) + optionalFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) +} + +declare global { + export interface XRPermissionStatus extends PermissionStatus { + granted: []; // FrozenArray (TODO?) ; (TODO) Also is this really `any`? + } + + // ## Input sources + + export interface XRInputSource { + readonly handedness: XRHandedness; + readonly targetRayMode: XRTargetRayMode; + readonly targetRaySpace: XRSpace; + readonly gripSpace: XRSpace | null; + readonly profiles: DOMString[]; // FrozenArray in the doc (TODO?) + } + + // This is actually a novel data structure which emulates a JS array (e.g. getter + `.length`) + // but it is not an array (TODO) + export type XRInputSourceArray = XRInputSource[]; + + // ## View + + export interface XRView { + readonly eye: XREye; + readonly projectionMatrix: Float32Array; + readonly transform: XRRigidTransform; + } + + // ## Spaces + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + export interface XRSpace extends EventTarget {} + + export interface XRReferenceSpace extends XRSpace { + getOffsetReferenceSpace( + originOffset: XRRigidTransform + ): XRReferenceSpace; + onreset: EventHandler; + } + + export interface XRBoundedReferenceSpace extends XRReferenceSpace { + readonly boundsGeometry: DOMPointReadOnly[]; // FrozenArray (TODO?) + } + + // ## Poses + + export interface XRPose { + readonly transform: XRRigidTransform; + readonly emulatedPosition: boolean; + } + + export interface XRViewerPose extends XRPose { + readonly views: XRView[]; // FrozenArray in the docs (TODO?) + } + + // ## Frames + + export interface XRFrame { + readonly session: XRSession; + getViewerPose(referenceSpace: XRReferenceSpace): XRViewerPose | null; + getPose(space: XRSpace, baseSpace: XRSpace): XRPose | null; + } +} + +export type XRFrameRequestCallback = ( + time: DOMHighResTimeStamp, + frame: XRFrame +) => void; + +// ## WebGL interop + +export interface XRWebGLLayerInit { + antialias?: boolean; + depth?: boolean; + stencil?: boolean; + alpha?: boolean; + ignoreDepthValues?: boolean; + framebufferScaleFactor?: number; +} + +declare global { + export class XRWebGLLayer { + constructor( + session: XRSession, + context: XRWebGLRenderingContext, + layerInit?: XRWebGLLayerInit + ); + readonly antialias: boolean; + readonly ignoreDepthValues: boolean; + readonly framebuffer?: WebGLFramebuffer; + readonly framebufferWidth: number; + readonly framebufferHeight: number; + + getViewport(view: XRView): XRViewport | null; + static getNativeFramebufferScaleFactor(session: XRSession): number; + } +} + +// ## Session + +export interface XRRenderStateInit { + depthNear?: number; + depthFar?: number; + inlineVerticalFieldOfView?: number; + baseLayer?: XRWebGLLayer | null; +} + +declare global { + export interface XRRenderState { + readonly depthNear: number; + readonly depthFar: number; + readonly inlineVerticalFieldOfView?: number; + readonly baseLayer?: XRWebGLLayer; + } + + export interface XRSession extends EventTarget { + readonly visibilityState: XRVisibilityState; + readonly renderState: XRRenderState; + readonly inputSources: XRInputSourceArray; + + // Methods + updateRenderState(state?: XRRenderStateInit): void; + requestReferenceSpace( + type: XRReferenceSpaceType + ): Promise; + + requestAnimationFrame(callback: XRFrameRequestCallback): number; + cancelAnimationFrame(handle: number): void; + + end(): Promise; + + environmentBlendMode: string; + + // Events + onend: EventHandler; + oninputsourceschange: EventHandler; + onselect: EventHandler; + onselectstart: EventHandler; + onselectend: EventHandler; + onsqueeze: EventHandler; + onsqueezestart: EventHandler; + onsqueezeend: EventHandler; + onvisibilitychange: EventHandler; + } + + // ## System + + export interface XRSystem extends EventTarget { + isSessionSupported(mode: XRSessionMode): Promise; + requestSession( + mode: XRSessionMode, + options?: XRSessionInit + ): Promise; + ondevicechange: EventHandler; + addEventListener( + type: "devicechange", + listener: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions + ): void; + } + + // ## Updates to existing objects + interface Navigator { + /** + * Optional because WebXR support is limited across browsers + */ + xr?: XRSystem; + } + + interface GLESRenderingContext { + /** + * Optional because WebXR support is limited across browsers + */ + makeXRCompatible?(): Promise; + } + + interface Window { + XRRigidTransform?: typeof XRRigidTransform; + XRWebGLLayer?: typeof XRWebGLLayer; + XRSessionEvent?: typeof XRSessionEvent; + XRInputSourceEvent?: typeof XRInputSourceEvent; + XRInputSourcesChangeEvent?: typeof XRInputSourcesChangeEvent; + XRReferenceSpaceEvent?: typeof XRReferenceSpaceEvent; + } +} diff --git a/src/gles.d.ts b/src/gles.d.ts new file mode 100644 index 0000000..27c714e --- /dev/null +++ b/src/gles.d.ts @@ -0,0 +1,802 @@ +/* eslint-disable no-unused-vars */ +/** + * Interface for GL-ES3 Typization + * (yet to be implemented in ts-spec) + */ + +interface GLESObject { } + +interface GLESActiveInfo { + readonly name: string; + readonly size: number; + readonly type: number; +} + +interface GLESBuffer extends GLESObject { } + +interface GLESFramebuffer extends GLESObject { } + +interface GLESProgram extends GLESObject { } + +interface GLESRenderbuffer extends GLESObject { } + +interface GLESShader extends GLESObject { } + +interface GLESShaderPrecisionFormat { + readonly precision: number; + readonly rangeMax: number; + readonly rangeMin: number; +} + +interface GLESTexture extends GLESObject { } + +interface GLESUniformLocation { } + +declare interface GLESRenderingContext extends WebGL2RenderingContext { + activeTexture(texture: number): void; + attachShader(program: GLESProgram | null, shader: GLESShader | null): void; + bindAttribLocation(program: GLESProgram | null, index: number, name: string): void; + bindBuffer(target: number, buffer: GLESBuffer | null): void; + bindFramebuffer(target: number, framebuffer: GLESFramebuffer | null): void; + bindRenderbuffer(target: number, renderbuffer: GLESRenderbuffer | null): void; + bindTexture(target: number, texture: GLESTexture | null): void; + blendColor(red: number, green: number, blue: number, alpha: number): void; + blendEquation(mode: number): void; + blendEquationSeparate(modeRGB: number, modeAlpha: number): void; + blendFunc(sfactor: number, dfactor: number): void; + blendFuncSeparate(srcRGB: number, dstRGB: number, srcAlpha: number, dstAlpha: number): void; + bufferData(target: number, size: number | ArrayBufferView | ArrayBuffer, usage: number): void; + bufferSubData(target: number, offset: number, data: ArrayBufferView | ArrayBuffer): void; + checkFramebufferStatus(target: number): number; + clear(mask: number): void; + clearColor(red: number, green: number, blue: number, alpha: number): void; + clearDepthf(depth: number): void; + clearStencil(s: number): void; + colorMask(red: boolean, green: boolean, blue: boolean, alpha: boolean): void; + compileShader(shader: GLESShader | null): void; + // compressedTexImage2D(target: number, level: number, internalformat: number, width: number, height: number, border: number, data: ArrayBufferView | ArrayBuffer): void; + // compressedTexSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, data: ArrayBufferView | ArrayBuffer): void; + copyTexImage2D(target: number, level: number, internalformat: number, x: number, y: number, width: number, height: number, border: number): void; + copyTexSubImage2D(target: number, level: number, xoffset: number, yoffset: number, x: number, y: number, width: number, height: number): void; + createBuffer(): GLESBuffer | null; + createFramebuffer(): GLESFramebuffer | null; + createProgram(): GLESProgram | null; + createRenderbuffer(): GLESRenderbuffer | null; + createShader(type: number): GLESShader | null; + createTexture(): GLESTexture | null; + cullFace(mode: number): void; + deleteBuffer(buffer: GLESBuffer | null): void; + deleteFramebuffer(framebuffer: GLESFramebuffer | null): void; + deleteProgram(program: GLESProgram | null): void; + deleteRenderbuffer(renderbuffer: GLESRenderbuffer | null): void; + deleteShader(shader: GLESShader | null): void; + deleteTexture(texture: GLESTexture | null): void; + depthFunc(func: number): void; + depthMask(flag: boolean): void; + depthRangef(zNear: number, zFar: number): void; + detachShader(program: GLESProgram | null, shader: GLESShader | null): void; + disable(cap: number): void; + disableVertexAttribArray(index: number): void; + drawArrays(mode: number, first: number, count: number): void; + drawElements(mode: number, count: number, type: number, offset: number): void; + enable(cap: number): void; + enableVertexAttribArray(index: number): void; + finish(): void; + flush(): void; + framebufferRenderbuffer(target: number, attachment: number, renderbuffertarget: number, renderbuffer: GLESRenderbuffer | null): void; + framebufferTexture2D(target: number, attachment: number, textarget: number, texture: GLESTexture | null, level: number): void; + frontFace(mode: number): void; + generateMipmap(target: number): void; + getActiveAttrib(program: GLESProgram | null, index: number): GLESActiveInfo | null; + getActiveUniform(program: GLESProgram | null, index: number): GLESActiveInfo | null; + getAttachedShaders(program: GLESProgram | null): GLESShader[] | null; + getAttribLocation(program: GLESProgram | null, name: string): number; + getBooleanv(pname: number): boolean; + getBufferParameteriv(target: number, pname: number): number; + getError(): number; + getFloatv(pname: number): number; + getFramebufferAttachmentParameteriv(target: number, attachment: number, pname: number): number; + getIntegerv(pname: number): number; + getProgramiv(program: GLESProgram, pname: number): number; + getProgramInfoLog(program: GLESProgram | null): string | null; + getRenderbufferParameteriv(program: GLESProgram | null, pname: number): number; + getShaderiv(shader: GLESShader, pname: number): number; + getShaderInfoLog(shader: GLESShader | null): string | null; + getShaderPrecisionFormat(shadertype: number, precisiontype: number): GLESShaderPrecisionFormat | null; + getShaderSource(shader: GLESShader | null): string | null; + getString(name: number): string; + getTexParameterfv(target: number, pname: number): number; + getTexParameteriv(target: number, pname: number): number; + getUniformfv(program: GLESProgram | null, location: GLESUniformLocation | null): number; + getUniformiv(program: GLESProgram | null, location: GLESUniformLocation | null): number; + getUniformLocation(program: GLESProgram | null, name: string): GLESUniformLocation | null; + getVertexAttribfv(index: number, pname: number): number; + getVertexAttribiv(index: number, pname: number): number; + getVertexAttribPointerv(index: number, pname: number): number; + hint(target: number, mode: number): void; + isBuffer(buffer: GLESBuffer | null): boolean; + isEnabled(cap: number): boolean; + isFramebuffer(framebuffer: GLESFramebuffer | null): boolean; + isProgram(program: GLESProgram | null): boolean; + isRenderbuffer(renderbuffer: GLESRenderbuffer | null): boolean; + isShader(shader: GLESShader | null): boolean; + isTexture(texture: GLESTexture | null): boolean; + lineWidth(width: number): void; + linkProgram(program: GLESProgram | null): void; + pixelStorei(pname: number, param: number | boolean): void; + polygonOffset(factor: number, units: number): void; + // readPixels(x: number, y: number, width: number, height: number, format: number, type: number, pixels: ArrayBufferView | null): void; + releaseShaderCompiler(): void; + renderbufferStorage(target: number, internalformat: number, width: number, height: number): void; + sampleCoverage(value: number, invert: boolean): void; + scissor(x: number, y: number, width: number, height: number): void; + shaderBinary(shader: GLESShader, binaryformat: number, bin: ArrayBuffer | ArrayBufferView): void; + shaderSource(shader: GLESShader | null, source: string): void; + stencilFunc(func: number, ref: number, mask: number): void; + stencilFuncSeparate(face: number, func: number, ref: number, mask: number): void; + stencilMask(mask: number): void; + stencilMaskSeparate(face: number, mask: number): void; + stencilOp(fail: number, zfail: number, zpass: number): void; + stencilOpSeparate(face: number, fail: number, zfail: number, zpass: number): void; + // texImage2D(target: number, level: number, internalformat: number, width: number, height: number, border: number, format: number, type: number, pixels: ArrayBufferView | null): void; + texParameterf(target: number, pname: number, param: number): void; + texParameterfv(target: number, pname: number, params: Float32Array): void; + texParameteri(target: number, pname: number, param: number): void; + texParameteriv(target: number, pname: number, params: Int32Array): void; + // texSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, type: number, pixels: ArrayBufferView | null): void; + uniform1f(location: GLESUniformLocation | null, x: number): void; + uniform1fv(location: GLESUniformLocation, v: Float32Array): void; + uniform1i(location: GLESUniformLocation | null, x: number): void; + uniform1iv(location: GLESUniformLocation, v: Int32Array): void; + uniform2f(location: GLESUniformLocation | null, x: number, y: number): void; + uniform2fv(location: GLESUniformLocation, v: Float32Array): void; + uniform2i(location: GLESUniformLocation | null, x: number, y: number): void; + uniform2iv(location: GLESUniformLocation, v: Int32Array): void; + uniform3f(location: GLESUniformLocation | null, x: number, y: number, z: number): void; + uniform3fv(location: GLESUniformLocation, v: Float32Array): void; + uniform3i(location: GLESUniformLocation | null, x: number, y: number, z: number): void; + uniform3iv(location: GLESUniformLocation, v: Int32Array): void; + uniform4f(location: GLESUniformLocation | null, x: number, y: number, z: number, w: number): void; + uniform4fv(location: GLESUniformLocation, v: Float32Array): void; + uniform4i(location: GLESUniformLocation | null, x: number, y: number, z: number, w: number): void; + uniform4iv(location: GLESUniformLocation, v: Int32Array): void; + uniformMatrix2fv(location: GLESUniformLocation, transpose: boolean, value: Float32Array): void; + uniformMatrix3fv(location: GLESUniformLocation, transpose: boolean, value: Float32Array): void; + uniformMatrix4fv(location: GLESUniformLocation, transpose: boolean, value: Float32Array): void; + useProgram(program: GLESProgram | null): void; + validateProgram(program: GLESProgram | null): void; + vertexAttrib1f(indx: number, x: number): void; + vertexAttrib1fv(indx: number, values: Float32Array): void; + vertexAttrib2f(indx: number, x: number, y: number): void; + vertexAttrib2fv(indx: number, values: Float32Array): void; + vertexAttrib3f(indx: number, x: number, y: number, z: number): void; + vertexAttrib3fv(indx: number, values: Float32Array): void; + vertexAttrib4f(indx: number, x: number, y: number, z: number, w: number): void; + vertexAttrib4fv(indx: number, values: Float32Array): void; + vertexAttribPointer(indx: number, size: number, type: number, normalized: boolean, stride: number, offset: number): void; + viewport(x: number, y: number, width: number, height: number): void; + + readonly DEPTH_BUFFER_BIT: number; + readonly STENCIL_BUFFER_BIT: number; + readonly COLOR_BUFFER_BIT: number; + readonly FALSE: number; + readonly TRUE: number; + readonly POINTS: number; + readonly LINES: number; + readonly LINE_LOOP: number; + readonly LINE_STRIP: number; + readonly TRIANGLES: number; + readonly TRIANGLE_STRIP: number; + readonly TRIANGLE_FAN: number; + readonly ZERO: number; + readonly ONE: number; + readonly SRC_COLOR: number; + readonly ONE_MINUS_SRC_COLOR: number; + readonly SRC_ALPHA: number; + readonly ONE_MINUS_SRC_ALPHA: number; + readonly DST_ALPHA: number; + readonly ONE_MINUS_DST_ALPHA: number; + readonly DST_COLOR: number; + readonly ONE_MINUS_DST_COLOR: number; + readonly SRC_ALPHA_SATURATE: number; + readonly FUNC_ADD: number; + readonly BLEND_EQUATION: number; + readonly BLEND_EQUATION_RGB: number; + readonly BLEND_EQUATION_ALPHA: number; + readonly FUNC_SUBTRACT: number; + readonly FUNC_REVERSE_SUBTRACT: number; + readonly BLEND_DST_RGB: number; + readonly BLEND_SRC_RGB: number; + readonly BLEND_DST_ALPHA: number; + readonly BLEND_SRC_ALPHA: number; + readonly CONSTANT_COLOR: number; + readonly ONE_MINUS_CONSTANT_COLOR: number; + readonly CONSTANT_ALPHA: number; + readonly ONE_MINUS_CONSTANT_ALPHA: number; + readonly BLEND_COLOR: number; + readonly ARRAY_BUFFER: number; + readonly ELEMENT_ARRAY_BUFFER: number; + readonly ARRAY_BUFFER_BINDING: number; + readonly ELEMENT_ARRAY_BUFFER_BINDING: number; + readonly STREAM_DRAW: number; + readonly STATIC_DRAW: number; + readonly DYNAMIC_DRAW: number; + readonly BUFFER_SIZE: number; + readonly BUFFER_USAGE: number; + readonly CURRENT_VERTEX_ATTRIB: number; + readonly FRONT: number; + readonly BACK: number; + readonly FRONT_AND_BACK: number; + readonly TEXTURE_2D: number; + readonly CULL_FACE: number; + readonly BLEND: number; + readonly DITHER: number; + readonly STENCIL_TEST: number; + readonly DEPTH_TEST: number; + readonly SCISSOR_TEST: number; + readonly POLYGON_OFFSET_FILL: number; + readonly SAMPLE_ALPHA_TO_COVERAGE: number; + readonly SAMPLE_COVERAGE: number; + readonly NO_ERROR: number; + readonly INVALID_ENUM: number; + readonly INVALID_VALUE: number; + readonly INVALID_OPERATION: number; + readonly OUT_OF_MEMORY: number; + readonly CW: number; + readonly CCW: number; + readonly LINE_WIDTH: number; + readonly ALIASED_POINT_SIZE_RANGE: number; + readonly ALIASED_LINE_WIDTH_RANGE: number; + readonly CULL_FACE_MODE: number; + readonly FRONT_FACE: number; + readonly DEPTH_RANGE: number; + readonly DEPTH_WRITEMASK: number; + readonly DEPTH_CLEAR_VALUE: number; + readonly DEPTH_FUNC: number; + readonly STENCIL_CLEAR_VALUE: number; + readonly STENCIL_FUNC: number; + readonly STENCIL_FAIL: number; + readonly STENCIL_PASS_DEPTH_FAIL: number; + readonly STENCIL_PASS_DEPTH_PASS: number; + readonly STENCIL_REF: number; + readonly STENCIL_VALUE_MASK: number; + readonly STENCIL_WRITEMASK: number; + readonly STENCIL_BACK_FUNC: number; + readonly STENCIL_BACK_FAIL: number; + readonly STENCIL_BACK_PASS_DEPTH_FAIL: number; + readonly STENCIL_BACK_PASS_DEPTH_PASS: number; + readonly STENCIL_BACK_REF: number; + readonly STENCIL_BACK_VALUE_MASK: number; + readonly STENCIL_BACK_WRITEMASK: number; + readonly VIEWPORT: number; + readonly SCISSOR_BOX: number; + readonly COLOR_CLEAR_VALUE: number; + readonly COLOR_WRITEMASK: number; + readonly UNPACK_ALIGNMENT: number; + readonly PACK_ALIGNMENT: number; + readonly MAX_TEXTURE_SIZE: number; + readonly MAX_VIEWPORT_DIMS: number; + readonly SUBPIXEL_BITS: number; + readonly RED_BITS: number; + readonly GREEN_BITS: number; + readonly BLUE_BITS: number; + readonly ALPHA_BITS: number; + readonly DEPTH_BITS: number; + readonly STENCIL_BITS: number; + readonly POLYGON_OFFSET_UNITS: number; + readonly POLYGON_OFFSET_FACTOR: number; + readonly TEXTURE_BINDING_2D: number; + readonly SAMPLE_BUFFERS: number; + readonly SAMPLES: number; + readonly SAMPLE_COVERAGE_VALUE: number; + readonly SAMPLE_COVERAGE_INVERT: number; + readonly NUM_COMPRESSED_TEXTURE_FORMATS: number; + readonly COMPRESSED_TEXTURE_FORMATS: number; + readonly DONT_CARE: number; + readonly FASTEST: number; + readonly NICEST: number; + readonly GENERATE_MIPMAP_HINT: number; + readonly BYTE: number; + readonly UNSIGNED_BYTE: number; + readonly SHORT: number; + readonly UNSIGNED_SHORT: number; + readonly INT: number; + readonly UNSIGNED_INT: number; + readonly FLOAT: number; + readonly FIXED: number; + readonly DEPTH_COMPONENT: number; + readonly ALPHA: number; + readonly RGB: number; + readonly RGBA: number; + readonly LUMINANCE: number; + readonly LUMINANCE_ALPHA: number; + readonly UNSIGNED_SHORT_4_4_4_4: number; + readonly UNSIGNED_SHORT_5_5_5_1: number; + readonly UNSIGNED_SHORT_5_6_5: number; + readonly FRAGMENT_SHADER: number; + readonly VERTEX_SHADER: number; + readonly MAX_VERTEX_ATTRIBS: number; + readonly MAX_VERTEX_UNIFORM_VECTORS: number; + readonly MAX_VARYING_VECTORS: number; + readonly MAX_COMBINED_TEXTURE_IMAGE_UNITS: number; + readonly MAX_VERTEX_TEXTURE_IMAGE_UNITS: number; + readonly MAX_TEXTURE_IMAGE_UNITS: number; + readonly MAX_FRAGMENT_UNIFORM_VECTORS: number; + readonly SHADER_TYPE: number; + readonly DELETE_STATUS: number; + readonly LINK_STATUS: number; + readonly VALIDATE_STATUS: number; + readonly ATTACHED_SHADERS: number; + readonly ACTIVE_UNIFORMS: number; + readonly ACTIVE_UNIFORM_MAX_LENGTH: number; + readonly ACTIVE_ATTRIBUTES: number; + readonly ACTIVE_ATTRIBUTE_MAX_LENGTH: number; + readonly SHADING_LANGUAGE_VERSION: number; + readonly CURRENT_PROGRAM: number; + readonly NEVER: number; + readonly LESS: number; + readonly EQUAL: number; + readonly LEQUAL: number; + readonly GREATER: number; + readonly NOTEQUAL: number; + readonly GEQUAL: number; + readonly ALWAYS: number; + readonly KEEP: number; + readonly REPLACE: number; + readonly INCR: number; + readonly DECR: number; + readonly INVERT: number; + readonly INCR_WRAP: number; + readonly DECR_WRAP: number; + readonly VENDOR: number; + readonly RENDERER: number; + readonly VERSION: number; + readonly EXTENSIONS: number; + readonly NEAREST: number; + readonly LINEAR: number; + readonly NEAREST_MIPMAP_NEAREST: number; + readonly LINEAR_MIPMAP_NEAREST: number; + readonly NEAREST_MIPMAP_LINEAR: number; + readonly LINEAR_MIPMAP_LINEAR: number; + readonly TEXTURE_MAG_FILTER: number; + readonly TEXTURE_MIN_FILTER: number; + readonly TEXTURE_WRAP_S: number; + readonly TEXTURE_WRAP_T: number; + readonly TEXTURE: number; + readonly TEXTURE_CUBE_MAP: number; + readonly TEXTURE_BINDING_CUBE_MAP: number; + readonly TEXTURE_CUBE_MAP_POSITIVE_X: number; + readonly TEXTURE_CUBE_MAP_NEGATIVE_X: number; + readonly TEXTURE_CUBE_MAP_POSITIVE_Y: number; + readonly TEXTURE_CUBE_MAP_NEGATIVE_Y: number; + readonly TEXTURE_CUBE_MAP_POSITIVE_Z: number; + readonly TEXTURE_CUBE_MAP_NEGATIVE_Z: number; + readonly MAX_CUBE_MAP_TEXTURE_SIZE: number; + readonly TEXTURE0: number; + readonly TEXTURE1: number; + readonly TEXTURE2: number; + readonly TEXTURE3: number; + readonly TEXTURE4: number; + readonly TEXTURE5: number; + readonly TEXTURE6: number; + readonly TEXTURE7: number; + readonly TEXTURE8: number; + readonly TEXTURE9: number; + readonly TEXTURE10: number; + readonly TEXTURE11: number; + readonly TEXTURE12: number; + readonly TEXTURE13: number; + readonly TEXTURE14: number; + readonly TEXTURE15: number; + readonly TEXTURE16: number; + readonly TEXTURE17: number; + readonly TEXTURE18: number; + readonly TEXTURE19: number; + readonly TEXTURE20: number; + readonly TEXTURE21: number; + readonly TEXTURE22: number; + readonly TEXTURE23: number; + readonly TEXTURE24: number; + readonly TEXTURE25: number; + readonly TEXTURE26: number; + readonly TEXTURE27: number; + readonly TEXTURE28: number; + readonly TEXTURE29: number; + readonly TEXTURE30: number; + readonly TEXTURE31: number; + readonly ACTIVE_TEXTURE: number; + readonly REPEAT: number; + readonly CLAMP_TO_EDGE: number; + readonly MIRRORED_REPEAT: number; + readonly FLOAT_VEC2: number; + readonly FLOAT_VEC3: number; + readonly FLOAT_VEC4: number; + readonly INT_VEC2: number; + readonly INT_VEC3: number; + readonly INT_VEC4: number; + readonly BOOL: number; + readonly BOOL_VEC2: number; + readonly BOOL_VEC3: number; + readonly BOOL_VEC4: number; + readonly FLOAT_MAT2: number; + readonly FLOAT_MAT3: number; + readonly FLOAT_MAT4: number; + readonly SAMPLER_2D: number; + readonly SAMPLER_CUBE: number; + readonly VERTEX_ATTRIB_ARRAY_ENABLED: number; + readonly VERTEX_ATTRIB_ARRAY_SIZE: number; + readonly VERTEX_ATTRIB_ARRAY_STRIDE: number; + readonly VERTEX_ATTRIB_ARRAY_TYPE: number; + readonly VERTEX_ATTRIB_ARRAY_NORMALIZED: number; + readonly VERTEX_ATTRIB_ARRAY_POINTER: number; + readonly VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: number; + readonly IMPLEMENTATION_COLOR_READ_TYPE: number; + readonly IMPLEMENTATION_COLOR_READ_FORMAT: number; + readonly COMPILE_STATUS: number; + readonly INFO_LOG_LENGTH: number; + readonly SHADER_SOURCE_LENGTH: number; + readonly SHADER_COMPILER: number; + readonly SHADER_BINARY_FORMATS: number; + readonly NUM_SHADER_BINARY_FORMATS: number; + readonly LOW_FLOAT: number; + readonly MEDIUM_FLOAT: number; + readonly HIGH_FLOAT: number; + readonly LOW_INT: number; + readonly MEDIUM_INT: number; + readonly HIGH_INT: number; + readonly FRAMEBUFFER: number; + readonly RENDERBUFFER: number; + readonly RGBA4: number; + readonly RGB5_A1: number; + readonly RGB565: number; + readonly DEPTH_COMPONENT16: number; + readonly STENCIL_INDEX8: number; + readonly RENDERBUFFER_WIDTH: number; + readonly RENDERBUFFER_HEIGHT: number; + readonly RENDERBUFFER_INTERNAL_FORMAT: number; + readonly RENDERBUFFER_RED_SIZE: number; + readonly RENDERBUFFER_GREEN_SIZE: number; + readonly RENDERBUFFER_BLUE_SIZE: number; + readonly RENDERBUFFER_ALPHA_SIZE: number; + readonly RENDERBUFFER_DEPTH_SIZE: number; + readonly RENDERBUFFER_STENCIL_SIZE: number; + readonly FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: number; + readonly FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: number; + readonly FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: number; + readonly FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: number; + readonly COLOR_ATTACHMENT0: number; + readonly DEPTH_ATTACHMENT: number; + readonly STENCIL_ATTACHMENT: number; + readonly NONE: number; + readonly FRAMEBUFFER_COMPLETE: number; + readonly FRAMEBUFFER_INCOMPLETE_ATTACHMENT: number; + readonly FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: number; + readonly FRAMEBUFFER_INCOMPLETE_DIMENSIONS: number; + readonly FRAMEBUFFER_UNSUPPORTED: number; + readonly FRAMEBUFFER_BINDING: number; + readonly RENDERBUFFER_BINDING: number; + readonly MAX_RENDERBUFFER_SIZE: number; + readonly INVALID_FRAMEBUFFER_OPERATION: number; + readonly READ_BUFFER: number; + readonly UNPACK_ROW_LENGTH: number; + readonly UNPACK_SKIP_ROWS: number; + readonly UNPACK_SKIP_PIXELS: number; + readonly PACK_ROW_LENGTH: number; + readonly PACK_SKIP_ROWS: number; + readonly PACK_SKIP_PIXELS: number; + readonly COLOR: number; + readonly DEPTH: number; + readonly STENCIL: number; + readonly RED: number; + readonly RGB8: number; + readonly RGBA8: number; + readonly RGB10_A2: number; + readonly TEXTURE_BINDING_3D: number; + readonly UNPACK_SKIP_IMAGES: number; + readonly UNPACK_IMAGE_HEIGHT: number; + readonly TEXTURE_3D: number; + readonly TEXTURE_WRAP_R: number; + readonly MAX_3D_TEXTURE_SIZE: number; + readonly UNSIGNED_INT_2_10_10_10_REV: number; + readonly MAX_ELEMENTS_VERTICES: number; + readonly MAX_ELEMENTS_INDICES: number; + readonly TEXTURE_MIN_LOD: number; + readonly TEXTURE_MAX_LOD: number; + readonly TEXTURE_BASE_LEVEL: number; + readonly TEXTURE_MAX_LEVEL: number; + readonly MIN: number; + readonly MAX: number; + readonly DEPTH_COMPONENT24: number; + readonly MAX_TEXTURE_LOD_BIAS: number; + readonly TEXTURE_COMPARE_MODE: number; + readonly TEXTURE_COMPARE_FUNC: number; + readonly CURRENT_QUERY: number; + readonly QUERY_RESULT: number; + readonly QUERY_RESULT_AVAILABLE: number; + readonly BUFFER_MAPPED: number; + readonly BUFFER_MAP_POINTER: number; + readonly STREAM_READ: number; + readonly STREAM_COPY: number; + readonly STATIC_READ: number; + readonly STATIC_COPY: number; + readonly DYNAMIC_READ: number; + readonly DYNAMIC_COPY: number; + readonly MAX_DRAW_BUFFERS: number; + readonly DRAW_BUFFER0: number; + readonly DRAW_BUFFER1: number; + readonly DRAW_BUFFER2: number; + readonly DRAW_BUFFER3: number; + readonly DRAW_BUFFER4: number; + readonly DRAW_BUFFER5: number; + readonly DRAW_BUFFER6: number; + readonly DRAW_BUFFER7: number; + readonly DRAW_BUFFER8: number; + readonly DRAW_BUFFER9: number; + readonly DRAW_BUFFER10: number; + readonly DRAW_BUFFER11: number; + readonly DRAW_BUFFER12: number; + readonly DRAW_BUFFER13: number; + readonly DRAW_BUFFER14: number; + readonly DRAW_BUFFER15: number; + readonly MAX_FRAGMENT_UNIFORM_COMPONENTS: number; + readonly MAX_VERTEX_UNIFORM_COMPONENTS: number; + readonly SAMPLER_3D: number; + readonly SAMPLER_2D_SHADOW: number; + readonly FRAGMENT_SHADER_DERIVATIVE_HINT: number; + readonly PIXEL_PACK_BUFFER: number; + readonly PIXEL_UNPACK_BUFFER: number; + readonly PIXEL_PACK_BUFFER_BINDING: number; + readonly PIXEL_UNPACK_BUFFER_BINDING: number; + readonly FLOAT_MAT2x3: number; + readonly FLOAT_MAT2x4: number; + readonly FLOAT_MAT3x2: number; + readonly FLOAT_MAT3x4: number; + readonly FLOAT_MAT4x2: number; + readonly FLOAT_MAT4x3: number; + readonly SRGB: number; + readonly SRGB8: number; + readonly SRGB8_ALPHA8: number; + readonly COMPARE_REF_TO_TEXTURE: number; + readonly MAJOR_VERSION: number; + readonly MINOR_VERSION: number; + readonly NUM_EXTENSIONS: number; + readonly RGBA32F: number; + readonly RGB32F: number; + readonly RGBA16F: number; + readonly RGB16F: number; + readonly VERTEX_ATTRIB_ARRAY_INTEGER: number; + readonly MAX_ARRAY_TEXTURE_LAYERS: number; + readonly MIN_PROGRAM_TEXEL_OFFSET: number; + readonly MAX_PROGRAM_TEXEL_OFFSET: number; + readonly MAX_VARYING_COMPONENTS: number; + readonly TEXTURE_2D_ARRAY: number; + readonly TEXTURE_BINDING_2D_ARRAY: number; + readonly R11F_G11F_B10F: number; + readonly UNSIGNED_INT_10F_11F_11F_REV: number; + readonly RGB9_E5: number; + readonly UNSIGNED_INT_5_9_9_9_REV: number; + readonly TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: number; + readonly TRANSFORM_FEEDBACK_BUFFER_MODE: number; + readonly MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: number; + readonly TRANSFORM_FEEDBACK_VARYINGS: number; + readonly TRANSFORM_FEEDBACK_BUFFER_START: number; + readonly TRANSFORM_FEEDBACK_BUFFER_SIZE: number; + readonly TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: number; + readonly RASTERIZER_DISCARD: number; + readonly MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: number; + readonly MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: number; + readonly INTERLEAVED_ATTRIBS: number; + readonly SEPARATE_ATTRIBS: number; + readonly TRANSFORM_FEEDBACK_BUFFER: number; + readonly TRANSFORM_FEEDBACK_BUFFER_BINDING: number; + readonly RGBA32UI: number; + readonly RGB32UI: number; + readonly RGBA16UI: number; + readonly RGB16UI: number; + readonly RGBA8UI: number; + readonly RGB8UI: number; + readonly RGBA32I: number; + readonly RGB32I: number; + readonly RGBA16I: number; + readonly RGB16I: number; + readonly RGBA8I: number; + readonly RGB8I: number; + readonly RED_INTEGER: number; + readonly RGB_INTEGER: number; + readonly RGBA_INTEGER: number; + readonly SAMPLER_2D_ARRAY: number; + readonly SAMPLER_2D_ARRAY_SHADOW: number; + readonly SAMPLER_CUBE_SHADOW: number; + readonly UNSIGNED_INT_VEC2: number; + readonly UNSIGNED_INT_VEC3: number; + readonly UNSIGNED_INT_VEC4: number; + readonly INT_SAMPLER_2D: number; + readonly INT_SAMPLER_3D: number; + readonly INT_SAMPLER_CUBE: number; + readonly INT_SAMPLER_2D_ARRAY: number; + readonly UNSIGNED_INT_SAMPLER_2D: number; + readonly UNSIGNED_INT_SAMPLER_3D: number; + readonly UNSIGNED_INT_SAMPLER_CUBE: number; + readonly UNSIGNED_INT_SAMPLER_2D_ARRAY: number; + readonly BUFFER_ACCESS_FLAGS: number; + readonly BUFFER_MAP_LENGTH: number; + readonly BUFFER_MAP_OFFSET: number; + readonly DEPTH_COMPONENT32F: number; + readonly DEPTH32F_STENCIL8: number; + readonly FLOAT_32_UNSIGNED_INT_24_8_REV: number; + readonly FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: number; + readonly FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: number; + readonly FRAMEBUFFER_ATTACHMENT_RED_SIZE: number; + readonly FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: number; + readonly FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: number; + readonly FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: number; + readonly FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: number; + readonly FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: number; + readonly FRAMEBUFFER_DEFAULT: number; + readonly FRAMEBUFFER_UNDEFINED: number; + readonly DEPTH_STENCIL_ATTACHMENT: number; + readonly DEPTH_STENCIL: number; + readonly UNSIGNED_INT_24_8: number; + readonly DEPTH24_STENCIL8: number; + readonly UNSIGNED_NORMALIZED: number; + readonly DRAW_FRAMEBUFFER_BINDING: number; + readonly READ_FRAMEBUFFER: number; + readonly DRAW_FRAMEBUFFER: number; + readonly READ_FRAMEBUFFER_BINDING: number; + readonly RENDERBUFFER_SAMPLES: number; + readonly FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: number; + readonly MAX_COLOR_ATTACHMENTS: number; + readonly COLOR_ATTACHMENT1: number; + readonly COLOR_ATTACHMENT2: number; + readonly COLOR_ATTACHMENT3: number; + readonly COLOR_ATTACHMENT4: number; + readonly COLOR_ATTACHMENT5: number; + readonly COLOR_ATTACHMENT6: number; + readonly COLOR_ATTACHMENT7: number; + readonly COLOR_ATTACHMENT8: number; + readonly COLOR_ATTACHMENT9: number; + readonly COLOR_ATTACHMENT10: number; + readonly COLOR_ATTACHMENT11: number; + readonly COLOR_ATTACHMENT12: number; + readonly COLOR_ATTACHMENT13: number; + readonly COLOR_ATTACHMENT14: number; + readonly COLOR_ATTACHMENT15: number; + readonly COLOR_ATTACHMENT16: number; + readonly COLOR_ATTACHMENT17: number; + readonly COLOR_ATTACHMENT18: number; + readonly COLOR_ATTACHMENT19: number; + readonly COLOR_ATTACHMENT20: number; + readonly COLOR_ATTACHMENT21: number; + readonly COLOR_ATTACHMENT22: number; + readonly COLOR_ATTACHMENT23: number; + readonly COLOR_ATTACHMENT24: number; + readonly COLOR_ATTACHMENT25: number; + readonly COLOR_ATTACHMENT26: number; + readonly COLOR_ATTACHMENT27: number; + readonly COLOR_ATTACHMENT28: number; + readonly COLOR_ATTACHMENT29: number; + readonly COLOR_ATTACHMENT30: number; + readonly COLOR_ATTACHMENT31: number; + readonly FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: number; + readonly MAX_SAMPLES: number; + readonly HALF_FLOAT: number; + readonly MAP_READ_BIT: number; + readonly MAP_WRITE_BIT: number; + readonly MAP_INVALIDATE_RANGE_BIT: number; + readonly MAP_INVALIDATE_BUFFER_BIT: number; + readonly MAP_FLUSH_EXPLICIT_BIT: number; + readonly MAP_UNSYNCHRONIZED_BIT: number; + readonly RG: number; + readonly RG_INTEGER: number; + readonly R8: number; + readonly RG8: number; + readonly R16F: number; + readonly R32F: number; + readonly RG16F: number; + readonly RG32F: number; + readonly R8I: number; + readonly R8UI: number; + readonly R16I: number; + readonly R16UI: number; + readonly R32I: number; + readonly R32UI: number; + readonly RG8I: number; + readonly RG8UI: number; + readonly RG16I: number; + readonly RG16UI: number; + readonly RG32I: number; + readonly RG32UI: number; + readonly VERTEX_ARRAY_BINDING: number; + readonly R8_SNORM: number; + readonly RG8_SNORM: number; + readonly RGB8_SNORM: number; + readonly RGBA8_SNORM: number; + readonly SIGNED_NORMALIZED: number; + readonly PRIMITIVE_RESTART_FIXED_INDEX: number; + readonly COPY_READ_BUFFER: number; + readonly COPY_WRITE_BUFFER: number; + readonly COPY_READ_BUFFER_BINDING: number; + readonly COPY_WRITE_BUFFER_BINDING: number; + readonly UNIFORM_BUFFER: number; + readonly UNIFORM_BUFFER_BINDING: number; + readonly UNIFORM_BUFFER_START: number; + readonly UNIFORM_BUFFER_SIZE: number; + readonly MAX_VERTEX_UNIFORM_BLOCKS: number; + readonly MAX_FRAGMENT_UNIFORM_BLOCKS: number; + readonly MAX_COMBINED_UNIFORM_BLOCKS: number; + readonly MAX_UNIFORM_BUFFER_BINDINGS: number; + readonly MAX_UNIFORM_BLOCK_SIZE: number; + readonly MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: number; + readonly MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: number; + readonly UNIFORM_BUFFER_OFFSET_ALIGNMENT: number; + readonly ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: number; + readonly ACTIVE_UNIFORM_BLOCKS: number; + readonly UNIFORM_TYPE: number; + readonly UNIFORM_SIZE: number; + readonly UNIFORM_NAME_LENGTH: number; + readonly UNIFORM_BLOCK_INDEX: number; + readonly UNIFORM_OFFSET: number; + readonly UNIFORM_ARRAY_STRIDE: number; + readonly UNIFORM_MATRIX_STRIDE: number; + readonly UNIFORM_IS_ROW_MAJOR: number; + readonly UNIFORM_BLOCK_BINDING: number; + readonly UNIFORM_BLOCK_DATA_SIZE: number; + readonly UNIFORM_BLOCK_NAME_LENGTH: number; + readonly UNIFORM_BLOCK_ACTIVE_UNIFORMS: number; + readonly UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: number; + readonly UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: number; + readonly UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: number; + readonly INVALID_INDEX: number; + readonly MAX_VERTEX_OUTPUT_COMPONENTS: number; + readonly MAX_FRAGMENT_INPUT_COMPONENTS: number; + readonly MAX_SERVER_WAIT_TIMEOUT: number; + readonly OBJECT_TYPE: number; + readonly SYNC_CONDITION: number; + readonly SYNC_STATUS: number; + readonly SYNC_FLAGS: number; + readonly SYNC_FENCE: number; + readonly SYNC_GPU_COMMANDS_COMPLETE: number; + readonly UNSIGNALED: number; + readonly SIGNALED: number; + readonly ALREADY_SIGNALED: number; + readonly TIMEOUT_EXPIRED: number; + readonly CONDITION_SATISFIED: number; + readonly WAIT_FAILED: number; + readonly SYNC_FLUSH_COMMANDS_BIT: number; + readonly TIMEOUT_IGNORED: number; + readonly VERTEX_ATTRIB_ARRAY_DIVISOR: number; + readonly ANY_SAMPLES_PASSED: number; + readonly ANY_SAMPLES_PASSED_CONSERVATIVE: number; + readonly SAMPLER_BINDING: number; + readonly RGB10_A2UI: number; + readonly TEXTURE_SWIZZLE_R: number; + readonly TEXTURE_SWIZZLE_G: number; + readonly TEXTURE_SWIZZLE_B: number; + readonly TEXTURE_SWIZZLE_A: number; + readonly GREEN: number; + readonly BLUE: number; + readonly INT_2_10_10_10_REV: number; + readonly TRANSFORM_FEEDBACK: number; + readonly TRANSFORM_FEEDBACK_PAUSED: number; + readonly TRANSFORM_FEEDBACK_ACTIVE: number; + readonly TRANSFORM_FEEDBACK_BINDING: number; + readonly PROGRAM_BINARY_RETRIEVABLE_HINT: number; + readonly PROGRAM_BINARY_LENGTH: number; + readonly NUM_PROGRAM_BINARY_FORMATS: number; + readonly PROGRAM_BINARY_FORMATS: number; + readonly COMPRESSED_R11_EAC: number; + readonly COMPRESSED_SIGNED_R11_EAC: number; + readonly COMPRESSED_RG11_EAC: number; + readonly COMPRESSED_SIGNED_RG11_EAC: number; + readonly COMPRESSED_RGB8_ETC2: number; + readonly COMPRESSED_SRGB8_ETC2: number; + readonly COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: number; + readonly COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: number; + readonly COMPRESSED_RGBA8_ETC2_EAC: number; + readonly COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: number; + readonly TEXTURE_IMMUTABLE_FORMAT: number; + readonly MAX_ELEMENT_INDEX: number; + readonly NUM_SAMPLE_COUNTS: number; + readonly TEXTURE_IMMUTABLE_LEVELS: number; + readonly ES_VERSION_2_0: number; +} diff --git a/src/three/shader/loader/glsl.d.ts b/src/glsl.d.ts similarity index 100% rename from src/three/shader/loader/glsl.d.ts rename to src/glsl.d.ts diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..103e734 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,28 @@ + +// Offline-Worker entry-point +export * from './offline/OfflineHelper'; + +// custom effects +export * from './three'; + +// three.ts lib +export * from './three.ts/src'; + +// Web-AssemblyScript entry-point +export * from './wasc-worker'; + +// audio processing +export * from './weas'; + +// single modules +export * from './CComponent'; +export * from './CSettings'; +export * from './FPSta'; +export * from './LoadHelper'; +export * from './ReloadHelper'; +export * from './Smallog'; +export * from './Util'; +export * from './WarnHelper'; +export * from './WEICUE'; +export * from './WEWA'; +export * from './XRHelper'; diff --git a/src/offline/Offline.ts b/src/offline/Offline.ts index e6b9022..2c9a20e 100644 --- a/src/offline/Offline.ts +++ b/src/offline/Offline.ts @@ -1,41 +1,41 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -* -* @description -* Generic Service Worker for Caching an app and making it available offline. -* Needs to be passed the `?jsonPath=` argument with a path to a file json. -* Everything else is explained in between... -* -* @see -* Read more: https://ponyfoo.com/articles/progressive-networking-serviceworker -*/ - -'use strict'; + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Generic Service Worker for Caching an app and making it available offline. + * Needs to be passed the `?jsonPath=` argument with a path to a file json. + * Everything else is explained in between... + * + * @see + * Read more: https://ponyfoo.com/articles/progressive-networking-serviceworker + */ + +"use strict"; const wrk: ServiceWorker = self as any; // A version number is useful when updating the worker logic, // allowing you to remove outdated cache entries during the update. -const wName = '[OfflineWorker]'; -const version = '::2.4'; -console.info(wName + 'executing.'); +const wName = "[OfflineWorker]"; +const version = "::2.4"; +console.info(wName + "executing."); // The install event fires when the service worker is first installed. // You can use this event to prepare the service worker to be able to serve // files while visitors are offline. -wrk.addEventListener('install', function(event: any) { - console.info(wName + ' install event in progress.'); +wrk.addEventListener("install", function (event: any) { + console.info(wName + " install event in progress."); // get the path for the .json file which contains the files to-be-cached // These resources will be downloaded and cached by the service worker // If any resource fails to be downloaded, then the service worker won't be installed. const sp = new URL(location.href).searchParams; - const jsonPath = sp.get('jsonPath'); + const jsonPath = sp.get("jsonPath"); // Using event.waitUntil(p) blocks the installation process on the provided // promise. If the promise is rejected, the service worker won't be installed. @@ -44,11 +44,14 @@ wrk.addEventListener('install', function(event: any) { fetch(jsonPath) .then((response) => response.json()) // after we received the files to store, we open the cache - .then((data) => caches.open(wName + version) - // then map the array and add one-by-one - .then((cache) => data.map((url) => cache.add(url)))) + .then((data) => + caches + .open(wName + version) + // then map the array and add one-by-one + .then((cache) => data.map((url) => cache.add(url))) + ) // log success - .then(() => console.info(wName + ' install completed')), + .then(() => console.info(wName + " install completed")) ); }); @@ -56,11 +59,15 @@ wrk.addEventListener('install', function(event: any) { // a resource. This isn't limited to `fetch` or even XMLHttpRequest. Instead, it // comprehends even the request for the HTML page on first load, as well as JS and // CSS resources, fonts, any images, etc. -wrk.addEventListener('fetch', function(event: any) { +wrk.addEventListener("fetch", function (event: any) { // console.info(wName + 'fetch event in progress.'); - if (event.request.method !== 'GET') { - console.info(wName + ' fetch event ignored.', event.request.method, event.request.url); + if (event.request.method !== "GET") { + console.info( + wName + " fetch event ignored.", + event.request.method, + event.request.url + ); return; } @@ -68,67 +75,87 @@ wrk.addEventListener('fetch', function(event: any) { // the request. Once the promise is settled, we can then provide a response // to the fetch request. event.respondWith( - caches.match(event.request) - .then(async (cached) => { - // return immediately if cache successfull - if (cached) { - console.info(wName + ' fetch event(cached): ', event.request.url); - return cached; - } - - // Else, use the preloaded response, if it's there - const preload = await event.preloadResponse; - if (preload) return preload; - - /** - * We copy the response before replying to the network request. - * @param {Response} response This will be stored on the ServiceWorker cache. - * @return {Response} cached - */ - function fetchedFromNetwork(response: Response) { - const cacheCopy = response.clone(); - console.info(wName + ' fetch response from network.', event.request.url); - // We open a cache to store the response for this request. - caches.open(wName + version) - .then((cache) => cache.put(event.request, cacheCopy)) - .then(() => console.info(wName + ' fetch response stored in cache.', event.request.url)); - // Return the response so that the promise is settled in fulfillment. - return response; - } - - /** - * When this method is called, it means we were unable to produce a response - * from either the cache or the network. This is our last opportunity - * to produce a meaningful response even when all else fails. - * E.g. - * - Test the Accept header and then return one of the `offlineFundamentals` - * e.g: `return caches.match('/some/cached/image.png')` - * - consider the origin. It's easier to decide what "unavailable" means - * for requests against your origins than for requests against a third party, - * such as an ad provider. - * - Generate a Response programmaticaly, as shown below, and return that. - * @return {Response} - */ - function unableToResolve() { - console.info(wName + ' fetch request failed in both cache and network.'); - return new Response('Service Unavailable', { - status: 503, - statusText: 'Service Unavailable', - headers: new Headers({ - 'Content-Type': 'text/html', - }), - }); - } - - // fallback to fetching from network - return fetch(event.request) + caches.match(event.request).then(async (cached) => { + // return immediately if cache successfull + if (cached) { + console.info( + wName + " fetch event(cached): ", + event.request.url + ); + return cached; + } + + // Else, use the preloaded response, if it's there + const preload = await event.preloadResponse; + if (preload) return preload; + + /** + * We copy the response before replying to the network request. + * @param {Response} response This will be stored on the ServiceWorker cache. + * @return {Response} cached + */ + function fetchedFromNetwork(response: Response) { + const cacheCopy = response.clone(); + console.info( + wName + " fetch response from network.", + event.request.url + ); + // We open a cache to store the response for this request. + caches + .open(wName + version) + .then((cache) => cache.put(event.request, cacheCopy)) + .then(() => + console.info( + wName + " fetch response stored in cache.", + event.request.url + ) + ); + // Return the response so that the promise is settled in fulfillment. + return response; + } + + /** + * When this method is called, it means we were unable to produce a response + * from either the cache or the network. This is our last opportunity + * to produce a meaningful response even when all else fails. + * E.g. + * - Test the Accept header and then return one of the `offlineFundamentals` + * e.g: `return caches.match('/some/cached/image.png')` + * - consider the origin. It's easier to decide what "unavailable" means + * for requests against your origins than for requests against a third party, + * such as an ad provider. + * - Generate a Response programmaticaly, as shown below, and return that. + * @return {Response} fallback + */ + function unableToResolve() { + console.info( + wName + " fetch request failed in both cache and network." + ); + return new Response("Service Unavailable", { + "status": 503, + "statusText": "Service Unavailable", + "headers": new Headers({ + "Content-Type": "text/html", + }), + }); + } + + // fallback to fetching from network + return ( + fetch(event.request) // We handle the network request with success and failure scenarios. .then(fetchedFromNetwork, unableToResolve) // we are done - .then(() => console.info(wName + ' fetch event(networked): ', event.request.url)) + .then(() => + console.info( + wName + " fetch event(networked): ", + event.request.url + ) + ) // We should catch errors on the fetchedFromNetwork handler as well. - .catch(unableToResolve); - }), + .catch(unableToResolve) + ); + }) ); }); @@ -138,28 +165,32 @@ wrk.addEventListener('fetch', function(event: any) { * this point you know that the new worker was installed correctly. In this example, * we delete old caches that don't match the version in the worker we just finished * installing. -*/ -wrk.addEventListener('activate', function(event: any) { - console.info(wName + ' activate event in progress.'); + */ +wrk.addEventListener("activate", function (event: any) { + console.info(wName + " activate event in progress."); event.waitUntil(async () => { // Feature-detect navigation preloads! - if (self['registration'] && self['registration'].navigationPreload) { - await self['registration'].navigationPreload.enable(); + if (self["registration"] && self["registration"].navigationPreload) { + await self["registration"].navigationPreload.enable(); } // This method will resolve an array of available cache keys. - caches.keys() + caches + .keys() // We return a promise that settles when all outdated caches are deleted. - .then((keys) => Promise.all( - // Filter by keys that don't start with the latest version prefix. - keys.filter((key) => !key.endsWith(version)) + .then((keys) => + Promise.all( + // Filter by keys that don't start with the latest version prefix. + keys + .filter((key) => !key.endsWith(version)) // Return a promise that's fulfilled when each outdated cache is deleted. - .map((key) => caches.delete(key)))) + .map((key) => caches.delete(key)) + ) + ) // completed - .then(() => console.info(wName + ' activate completed.')); + .then(() => console.info(wName + " activate completed.")); }); // Tell the active service worker to take control of the page immediately. - if (wrk['clients']) wrk['clients'].claim(); + if (wrk["clients"]) wrk["clients"].claim(); }); - diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index ad62b0a..21d31a1 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -1,91 +1,109 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ -/* eslint-disable no-unused-vars */ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ -import {Smallog} from '../Smallog'; +import { Smallog } from "../"; // this should pack the serviceworker like a webwoker. -import OfflineWorker from 'worker-loader!./Offline'; +import OfflineWorker from "worker-loader!./Offline"; -const oh = '[OfflineHelper] '; +const oh = "[OfflineHelper] "; /** -* @ignore -* Helper class for loading and registering the ServiceWorker -*
      -* the workerPath is the path to the compiled OfflineWorker js file, relative from the calling html file... -*
      -* the "?jsonPath=" argument will give the worker a "public available" json file, consisting of a string array. -*
      -* the array will contain all necessary files to run the app offline. -*
      -* the ServiceWorker will cache these files and automatically load them if the website is ran offline. -*
      -* ServiceWorker is a progressive technology. Some browsers will be unsupported... -* @public -*/ + * @ignore + * Helper class for loading and registering the ServiceWorker + *
      + * the workerPath is the path to the compiled OfflineWorker js file, relative from the calling html file... + *
      + * the "?jsonPath=" argument will give the worker a "public available" json file, consisting of a string array. + *
      + * the array will contain all necessary files to run the app offline. + *
      + * the ServiceWorker will cache these files and automatically load them if the website is ran offline. + *
      + * ServiceWorker is a progressive technology. Some browsers will be unsupported... + * @public + */ // function helper, so OfflineWorker is actually processed // eslint-disable-next-line require-jsdoc +// eslint-disable-next-line @typescript-eslint/no-unused-vars function DontRemove() { return new OfflineWorker(); -}; +} /** -* In order to intercept ALL fetch-requests offline, the scope "/" (root) is required. -* when you put in in a sub-directory like "/js/", the scope is also "/js/". -* Then, your HTTP Server will have to send the REPONSE HEADER `service-worker-allowed: /` -* otherwise it will cause an ERROR in your Browser. So: Putting the ServiceWorker in root folder is easiest. -* obviously with webpack, this causes a problem, when you are not outputting directly into the root dir... -* eslint-disable-next-line require-jsdoc -* -* @public -* @param {string} name -* @param {string} worker -* @param {string} oFile -* @return {Promise} -*/ -function register(name: string, worker: string = 'Offline.worker.js', oFile: string = 'offlinefiles.json'): Promise { - return new Promise(async (resolve) => { - if ('serviceWorker' in navigator) { + * @description + * In order to intercept ALL fetch-requests offline, the scope "/" (root) is required. + * when you put in in a sub-directory like "/js/", the scope is also "/js/". + * Then, your HTTP Server will have to send the REPONSE HEADER `service-worker-allowed: /` + * otherwise it will cause an ERROR in your Browser. So: Putting the ServiceWorker in root folder is easiest. + * obviously with webpack, this causes a problem, when you are not outputting directly into the root dir... + * eslint-disable-next-line require-jsdoc + * + * @public + * @param {string} name - the name of the worker + * @param {string} worker - the path to the worker + * @param {string} oFile - the path to the offline json file + * @return {Promise} finished + */ +function register( + name: string, + worker = "Offline.worker.js", + oFile = "offlinefiles.json" +): Promise { + return new Promise((resolve) => { + if ("serviceWorker" in navigator) { const workerPath = `${worker}?name=${name}&jsonPath=${oFile}`; - await navigator.serviceWorker.register(workerPath, {scope: '/'}) - .then(() => Smallog.info('service-worker registration complete.', oh), - () => Smallog.error('service-worker registration failure.', oh)) + navigator.serviceWorker + .register(workerPath, { scope: "/" }) + .then( + () => + Smallog.info( + "service-worker registration complete.", + oh + ), + (reason) => + Smallog.error( + "service-worker registration failur: " + reason, + oh + ) + ) .then(() => resolve(true)); return true; } else { - Smallog.error('not supported!', oh); + Smallog.error("not supported!", oh); resolve(false); } }); } /** -* unregister all service workers -* @return {Promise} finished -* @public -*/ + * unregister all service workers + * @return {Promise} finished + * @public + */ async function reset(): Promise { return new Promise((resolve) => { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.getRegistrations().then(async (registrations) => { - for (const registration of registrations) { - await registration.unregister(); - } - resolve(true); - }); + if ("serviceWorker" in navigator) { + navigator.serviceWorker + .getRegistrations() + .then(async (registrations) => { + for (const registration of registrations) { + await registration.unregister(); + } + resolve(true); + }); } else { - Smallog.error('not supported!', oh); + Smallog.error("not supported!", oh); resolve(false); } }); } -export const OfflineHelper = {register, reset}; +export const OfflineHelper = { register, reset }; diff --git a/src/offline/OfflinePlugin.js b/src/offline/OfflinePlugin.js index 4a65fd2..5568ab1 100644 --- a/src/offline/OfflinePlugin.js +++ b/src/offline/OfflinePlugin.js @@ -1,150 +1,163 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -* @ignore -*/ - + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * @ignore + */ -const fs = require('fs'); -const validate = require('schema-utils'); -const {Compilation} = require('webpack'); -const {RawSource} = require('webpack-sources'); +const fs = require("fs"); +const validate = require("schema-utils"); +const { Compilation } = require("webpack"); +const { RawSource } = require("webpack-sources"); -const pluginName = 'OfflinePlugin'; +const pluginName = "OfflinePlugin"; /** * schema for options object * @see {OfflinePlugin} */ const offlineSchema = { - type: 'object', - properties: { - staticdir: { - type: 'string', - }, - outfile: { - type: 'string', - }, - extrafiles: { - type: 'array', - }, - pretty: { - type: 'boolean', - }, - }, + type: "object", + properties: { + staticdir: { + type: "string", + }, + outfile: { + type: "string", + }, + extrafiles: { + type: "array", + }, + pretty: { + type: "boolean", + }, + }, }; /** -* list files recursively -* @param {strring} baseDir start directory -* @param {string} subDir sub directory -* @param {array} arrayOfFiles result files -* @return {array} arrayOfFiles -*/ + * list files recursively + * @param {strring} baseDir start directory + * @param {string} subDir sub directory + * @param {array} arrayOfFiles result files + * @return {array} arrayOfFiles + */ function getAllFiles(baseDir, subDir, arrayOfFiles) { - const sub = baseDir + '/' + subDir; - const files = fs.readdirSync(sub); - arrayOfFiles = arrayOfFiles || []; - files.forEach((file) => { - const fle = sub + '/' + file; - if (fs.statSync(fle).isDirectory()) { - arrayOfFiles = getAllFiles(baseDir, subDir + '/' + file, arrayOfFiles); - } else { - arrayOfFiles.push(subDir + '/' + file); - } - }); - return arrayOfFiles; + const sub = baseDir + "/" + subDir; + const files = fs.readdirSync(sub); + arrayOfFiles = arrayOfFiles || []; + files.forEach((file) => { + const fle = sub + "/" + file; + if (fs.statSync(fle).isDirectory()) { + arrayOfFiles = getAllFiles( + baseDir, + subDir + "/" + file, + arrayOfFiles + ); + } else { + arrayOfFiles.push(subDir + "/" + file); + } + }); + return arrayOfFiles; } /** -* This is a webpack plugin for easily making your webapp available Offline. -*
      -* It will simply output a json list of files to cache for the Service Worker on build. -*
      -* In your source, you just 'require' and 'Register()' the OfflineHelper. -*
      -* The OfflineHelper will then require the OfflineWorker in background. -*
      -*
      -* -* The OfflineWorker will then: -*
      -* 1. launch itself -*
      -* 2. load the 'offlinefiles.json' list -*
      -* 3. cache all files in it -*
      -* 4. return cached files if network fetching fails -*
      -*
      -* You can also ignore this plugin and create the 'offlinefiles.json' yourself. -*/ + * This is a webpack plugin for easily making your webapp available Offline. + *
      + * It will simply output a json list of files to cache for the Service Worker on build. + *
      + * In your source, you just 'require' and 'Register()' the OfflineHelper. + *
      + * The OfflineHelper will then require the OfflineWorker in background. + *
      + *
      + * + * The OfflineWorker will then: + *
      + * 1. launch itself + *
      + * 2. load the 'offlinefiles.json' list + *
      + * 3. cache all files in it + *
      + * 4. return cached files if network fetching fails + *
      + *
      + * You can also ignore this plugin and create the 'offlinefiles.json' yourself. + */ class OfflinePlugin { - options = {}; - - /** - * Intializes the plugin in the webpack build process - * @param {offliineSchema} options - */ - constructor(options = {}) { - validate.validate(offlineSchema, options); - this.options = options; - } - - /** - * Hook into the compilation process, - * find all target files and put them into a json file - * @param {Webpack.compiler} compiler object from webpack - */ - apply(compiler) { - let addedOnce = false; - // Specify the event hook to attach to - compiler.hooks.thisCompilation.tap(pluginName, (compilation) => - compilation.hooks.processAssets.tap({ - name: pluginName, - stage: Compilation.PROCESS_ASSETS_STAGE_ANALYSE, - }, () => { - if (addedOnce) return; - addedOnce = true; - - console.info('[' + pluginName + '] Gathering Infos...'); - - // list of all app-contents - const filelist = []; - - // add static files from folder - const sFiles = getAllFiles(this.options.staticdir, ''); - for (const staticFile in sFiles) { - if (!staticFile) continue; - filelist.push(sFiles[staticFile]); - } - - // Loop through all compiled assets, - // adding a new line item for each filename. - for (const filename in compilation.assets) { - if (!filename) continue; - filelist.push('/' + filename); - } - - // add additional files anyway? - if (this.options.extrafiles) { - this.options.extrafiles.map((ef) => filelist.push(ef)); - } - - // create the target file with all app-contents as json list - const jList = JSON.stringify(filelist, null, this.options.pretty ? 1 : 0); - compilation.emitAsset(this.options.outfile, new RawSource(jList)); - console.info('[' + pluginName + '] result: ' + jList); - - console.info('[' + pluginName + '] finished.'); - }, - )); - } + options = {}; + + /** + * Intializes the plugin in the webpack build process + * @param {offliineSchema} options + */ + constructor(options = {}) { + validate.validate(offlineSchema, options); + this.options = options; + } + + /** + * Hook into the compilation process, + * find all target files and put them into a json file + * @param {Webpack.compiler} compiler object from webpack + */ + apply(compiler) { + let addedOnce = false; + // Specify the event hook to attach to + compiler.hooks.thisCompilation.tap(pluginName, (compilation) => + compilation.hooks.processAssets.tap( + { + name: pluginName, + stage: Compilation.PROCESS_ASSETS_STAGE_ANALYSE, + }, + () => { + if (addedOnce) return; + addedOnce = true; + + console.info("[" + pluginName + "] Gathering Infos..."); + + // list of all app-contents + const filelist = []; + + // add static files from folder + const sFiles = getAllFiles(this.options.staticdir, ""); + for (const staticFile in sFiles) { + if (!staticFile) continue; + filelist.push(sFiles[staticFile]); + } + + // Loop through all compiled assets, + // adding a new line item for each filename. + for (const filename in compilation.assets) { + if (!filename) continue; + filelist.push("/" + filename); + } + + // add additional files anyway? + if (this.options.extrafiles) { + this.options.extrafiles.map((ef) => filelist.push(ef)); + } + + // create the target file with all app-contents as json list + const jList = JSON.stringify( + filelist, + null, + this.options.pretty ? 1 : 0 + ); + compilation.emitAsset( + this.options.outfile, + new RawSource(jList) + ); + console.info("[" + pluginName + "] result: " + jList); + + console.info("[" + pluginName + "] finished."); + } + ) + ); + } } module.exports = OfflinePlugin; diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js index 9fea7f1..d2ac34a 100644 --- a/src/renamer/RenamerPlugin.js +++ b/src/renamer/RenamerPlugin.js @@ -5,6 +5,10 @@ * Copyright (c) 2021 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. +* +* @todo +* - export normal strings? +* * @ignore */ @@ -36,12 +40,10 @@ class RenamerPlugin { // mapped name list: {key,value} nameMap = []; - // saved character count - savedChars = 0; /** * Intializes the plugin in the webpack build process - * @param {offliineSchema} options + * @param {offlineSchema} options - plugin options */ constructor(options = {}) { validate.validate(offlineSchema, options); @@ -50,8 +52,8 @@ class RenamerPlugin { /** * Get a random variable name, which does not exist in src and mappings - * @param {string} src - * @return {string} + * @param {string} src - source code + * @return {string} random variable name */ getRandomName(src) { const gen = '$x' + Math.random().toString(20).substr(2, 2 + Math.random() * 4); @@ -64,9 +66,9 @@ class RenamerPlugin { /** * Regex replace "match" function - * @param {string} source - * @param {string} match - * @return {string} + * @param {string} source - source code + * @param {string} match - regex match + * @return {string} replaced string */ replaceMatch(source, match) { let fnd = null; @@ -84,8 +86,8 @@ class RenamerPlugin { /** * randomize array - * @param {Array} array - * @return {Array} + * @param {Array} array - array to randomize + * @return {Array} randomized array */ shuffle(array) { let currentIndex = array.length; let randomIndex; @@ -119,7 +121,7 @@ class RenamerPlugin { // count all accessor usages, which could gain an advantage by shortening const processed = {}; - pd.match(/\.+[a-zA-Z0-9_]{5,}/g).forEach((element) => { + pd.match(/\.+[a-zA-Z0-9_]{6,}/g).forEach((element) => { const match = element; if (match.indexOf('.') != 0) return; // only dot at start allowed if (match.indexOf('..') == 0) return; // skip spread operator @@ -176,8 +178,6 @@ class RenamerPlugin { console.log('Potentially saving: ' + sum + ' chars.'); const oldPd = pd; - const oldStrict ='"use strict";'; - // replace all occurences for (let index = 0; index < filtered.length; index++) { @@ -191,7 +191,7 @@ class RenamerPlugin { const operations = []; const rgx = new RegExp('\\' + element.k, 'g'); let rm; - while (( rm = rgx.exec(newPd)) != null) { + while ((rm = rgx.exec(newPd)) != null) { const rIdx = rm.index; // match is invalid if it is followed by a alphanumeric char or _ @@ -231,7 +231,7 @@ class RenamerPlugin { // calculate new offset for following pending operations const off = (to - from) - re.length; - for (let idx = index +1; idx < operations.length; idx++) { + for (let idx = index + 1; idx < operations.length; idx++) { const op = operations[idx]; if (op.start > to - off) op.start -= off; if (op.end > to - off) op.end -= off; @@ -264,22 +264,34 @@ class RenamerPlugin { return r; } + function mayEncode(str) { + if(Math.random() > 0.5) { + return lib.JSFuck.encode(str); + } + return `'${str}'`; + } - let newStrict = oldStrict; + let newStrict = ""; const keys = Object.keys(globalMap); if (keys.length > 4) { const fk1 = lib.JSFuck.encode(rndOff.toString()); - const fk2 = lib.JSFuck.encode('split'); + const fk2 = mayEncode('split'); const fk3 = lib.JSFuck.encode(delim); - const func = `(s)=>{var r='';for(var i=0;i{var è='',ë=${fk5},ė=window;for(var ê=0,é=ė[${fk6}](é);ê<é[${fk4}];è+=ė[${fk7}][${tmp1}]((é[ê++][${tmp2}]())-(${fk1}))){}return è[${fk2}](${fk3})}`; + newStrict += `var ${splFunc}=${func},`; } keys.forEach((k) => { let val = globalMap[k]; if (val.length > 4) { if (keys.length > 4) { - val = `${splFunc}(${JSON.stringify(caesarCipher(val.join(delim)))})`; + val = `${splFunc}('${btoa(caesarCipher(val.join(delim)))}')`; } else { val = `"${val.join(delim)}".split('${delim}')`; } @@ -290,22 +302,22 @@ class RenamerPlugin { newStrict += ','; } if (newStrict.endsWith(';')) { - newStrict +='var '; + newStrict += 'var '; } newStrict += `${k}=${val}`; }); console.log(newStrict); - pd = pd.replace(oldStrict, newStrict + ';'); + pd = newStrict + ';' + pd; const lengDiff = oldPd.length - pd.length; const expDiff = lengDiff - sum; console.log('Replacement complete!'); let m = 'Actually saved: ' + lengDiff + ' chars. '; - if (expDiff > 0) m+= ' (' + expDiff + ' more than expected)'; - if (expDiff < 0) m+= ' (' + Math.abs(expDiff) + ' less than expected)'; + if (expDiff > 0) m += ' (' + expDiff + ' more than expected)'; + if (expDiff < 0) m += ' (' + Math.abs(expDiff) + ' less than expected)'; console.log(m); if (lengDiff < 0) { console.log('Did not save any chars! rolling back...'); @@ -317,10 +329,15 @@ class RenamerPlugin { /** * process via regex - * @param {string} source - * @return {string} + * @param {string} source - source code + * @return {string} processed source */ processString(source) { + if (typeof source !== 'string') { + console.error('Source no string: ', source); + return source; + } + const pd = source.replace(this.options.regex, (match) => this.replaceMatch(source, match)); // .replaceAll('const ', 'var '); const sa = this.shortenAccessors(pd); @@ -328,10 +345,35 @@ class RenamerPlugin { return sa; } + /** + * Process given source file object + * @param {Webpack.Compilation} compilation - webpack compilation + * @param {string} name - source file name + * @param {any} child - source file object + * @return {void} + */ + processSource(compilation, name, child) { + if (child._valueIsBuffer) { + console.log('Value is Buffer!'); + return; + } + const source = child.source._value; + const processed = this.processString(source); + + // if anything changed, update the processed asset + if (typeof processed === 'string' && source != processed) { + compilation.updateAsset(name, new RawSource(processed)); + // calculate saved memory + const savedChars = (source.length - processed.length); + console.info('[' + pluginName + '] Saved: ' + savedChars + ' chars'); + } + } + /** * Hook into the compilation process, * Replace regex matches with random strings * @param {Webpack.compiler} compiler object from webpack + * @return {void} */ apply(compiler) { compiler.hooks.emit.tap(pluginName, (compilation) => { @@ -345,20 +387,18 @@ class RenamerPlugin { // get the processed asset object / source const asset = compilation.getAsset(assetFile); - const source = asset.source._value; - const processed = this.processString(source); - - // if anything changed, update the processed asset - if (source != processed) { - compilation.updateAsset(assetFile, new RawSource(processed)); - // calculate saved memory - this.savedChars += (source.length - processed.length); + + if (asset.source._children) { + asset.source._children.forEach((child, i) => { + this.processSource(compilation, assetFile, child); + }); + } else { + this.processSource(compilation, assetFile, asset); } } // finish up console.info('[' + pluginName + '] Replaced: ', this.nameMap); - console.info('[' + pluginName + '] Saved: ' + this.savedChars + ' chars'); } catch (error) { console.info('[' + pluginName + '] Replace error: ', error); } diff --git a/src/three.ts b/src/three.ts index 8ae2769..5468b4c 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 8ae2769518566e5662f2eefedb5415ddc2c9462a +Subproject commit 5468b4cade2930ffdaefc68a4f4e2ac366b980e7 diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts index 8adf7eb..dd47a4f 100644 --- a/src/three/EffectComposer.ts +++ b/src/three/EffectComposer.ts @@ -4,9 +4,8 @@ * @author hexxone / https://hexx.one */ -import {LinearFilter, PerspectiveCamera, Quaternion, RGBAFormat, Scene, Vector2, WebGLRenderer, WebGLRenderTarget, XRFrame} from 'three'; -import {BasePass} from './pass/BasePass'; -import {RenderPass} from './pass/RenderPass'; +import {RenderPass, BasePass, LinearFilter, RGBAFormat, Scene, PerspectiveCamera, WebGLRenderer, Vector2, WebGLRenderTarget, Quaternion} from '../'; + const defaultParams = { minFilter: LinearFilter, @@ -50,14 +49,14 @@ export class EffectComposer { /** * Instantiate - * @param {Scene} scene - * @param {PerspectiveCamera} camera - * @param {WebGLRenderer} renderer - * @param {string} globalPrec - * @param {Color} clearCol - * @param {WebGLRenderTarget} renderTarget + * @param {Scene} scene Scene + * @param {PerspectiveCamera} camera camera + * @param {WebGLRenderer} renderer renderer + * @param {string} globalPrec global precision + * @param {Color} clearCol Color + * @param {WebGLRenderTarget} renderTarget target to Reset (optional) */ - constructor(scene: Scene, camera: PerspectiveCamera, renderer: WebGLRenderer, globalPrec: string = 'mediump', clearCol?: any, renderTarget?: WebGLRenderTarget) { + constructor(scene: Scene, camera: PerspectiveCamera, renderer: WebGLRenderer, globalPrec = 'mediump', clearCol?: any, renderTarget?: WebGLRenderTarget) { this.scene = scene; this.camera = camera; this.renderer = renderer; @@ -92,8 +91,9 @@ export class EffectComposer { } /** - * Precompile all shaders... - */ + * Precompile all shaders... + * @returns {void} + */ public precompile(): void { this.renderer.compile(this.scene, this.camera); this.passes.forEach((pass) => pass.prepare(this.renderer)); @@ -103,6 +103,7 @@ export class EffectComposer { * Append a shader to the chain * @public * @param {BasePass} p Shader to add + * @returns {void} */ public addPass(p: BasePass) { p.setSize(this.viewSize.width, this.viewSize.height); @@ -114,6 +115,7 @@ export class EffectComposer { * @public * @param {BasePass} p Shader to add * @param {number} index position + * @returns {void} */ public insertPass(p: BasePass, index: number) { p.setSize(this.viewSize.width, this.viewSize.height); @@ -122,7 +124,8 @@ export class EffectComposer { /** * Update clear color - * @param {any} clearCol Color + * @param {any} clearCol Color + * @returns {void} */ public setClearColor(clearCol: any) { this.normPass.clearColor = clearCol; @@ -133,6 +136,7 @@ export class EffectComposer { * Checks if the given shader should be rendererd to screen * @param {number} passIndex position * @return {boolean} + * @returns {void} */ private isLastEnabledPass(passIndex: number) { for (let i = passIndex + 1; i < this.passes.length; i++) { @@ -146,6 +150,7 @@ export class EffectComposer { * @public * @param {number} deltaTime if not given, will calculate its own * @param {XRFrame} frame Currently rendering XR frame? + * @returns {void} */ public render(deltaTime?: number, frame?: XRFrame) { // deltaTime value is in seconds @@ -154,19 +159,21 @@ export class EffectComposer { deltaTime = (dn - this.previousFrame) * 0.001; } this.previousFrame = dn; + const size = new Vector2(); - this.renderer.getSize( size ); + this.renderer.getSize(size); const currentRenderTarget = this.renderer.getRenderTarget(); + // has enabled passes? const hasTargets = this.passes.filter((p) => p.enabled).length > 0; // clear surface ? - if ( this.renderer.autoClear ) this.renderer.clear(); + if (this.renderer.autoClear) this.renderer.clear(); // do spilt rendering if (this.renderer.xr.isPresenting && frame !== null) { this.scene.updateMatrixWorld(); - if ( this.camera.parent === null ) this.camera.updateMatrixWorld(); + if (this.camera.parent === null) this.camera.updateMatrixWorld(); // update cameras const pose = frame.getViewerPose(this.renderer.xr.getReferenceSpace()); @@ -178,7 +185,7 @@ export class EffectComposer { // dont use native XR features now this.renderer.xr.enabled = false; - this.renderer.setScissorTest( true ); + this.renderer.setScissorTest(true); // render for (let i = 0; i < views.length; i++) { @@ -202,8 +209,8 @@ export class EffectComposer { // render const offX = viewSize * i; - this.renderer.setScissor( offX, 0, viewSize, size.height ); - this.renderer.setViewport( offX, 0, viewSize, size.height ); + this.renderer.setScissor(offX, 0, viewSize, size.height); + this.renderer.setViewport(offX, 0, viewSize, size.height); // pass buffers flipped to avoid swap this.xrPass.render(this.renderer, this.readBuffer, this.writeBuffer, false, !hasTargets); @@ -216,13 +223,13 @@ export class EffectComposer { } // reset features - this.renderer.setScissorTest( false ); + this.renderer.setScissorTest(false); this.renderer.xr.enabled = true; } else { // render default this.camera.rotation.set(0, 0, 0); - this.renderer.setScissor( 0, 0, size.width, size.height ); - this.renderer.setViewport( 0, 0, size.width, size.height ); + this.renderer.setScissor(0, 0, size.width, size.height); + this.renderer.setViewport(0, 0, size.width, size.height); // pass buffers flipped to avoid swap this.normPass.render(this.renderer, this.readBuffer, this.writeBuffer, false, !hasTargets); this.passes.forEach((pass, i) => { @@ -240,6 +247,7 @@ export class EffectComposer { * Destroy the current shader-chain * @public * @param {WebGLRenderTarget} renderTarget target to Reset (optional) + * @returns {void} */ public reset(renderTarget?: WebGLRenderTarget) { if (renderTarget === undefined) { @@ -267,6 +275,7 @@ export class EffectComposer { * @public * @param {number} width X * @param {number} height Y + * @returns {void} */ public setSize(width: number, height: number) { this.renderWrite.setSize(width, height); @@ -282,6 +291,7 @@ export class EffectComposer { * * This is a workaround to pass their data further down the render-chain * @ignore + * @returns {void} */ private swapBuffers() { const tmp = this.readBuffer; diff --git a/src/three/index.ts b/src/three/index.ts index 5394cfa..0736854 100644 --- a/src/three/index.ts +++ b/src/three/index.ts @@ -6,7 +6,6 @@ */ export * from './EffectComposer'; -export * from './XRHelper'; export * from './pass/BasePass'; export * from './pass/FullScreenHelper'; diff --git a/src/three/pass/BasePass.ts b/src/three/pass/BasePass.ts index 06ab87b..90d60eb 100644 --- a/src/three/pass/BasePass.ts +++ b/src/three/pass/BasePass.ts @@ -1,13 +1,12 @@ -import {WebGLRenderer} from 'three'; +import {WebGLRenderer, WebGLRenderTarget} from '../../'; /** * @author alteredq / http://alteredqualia.com/ * @author hexxone / https://hexx.one * * Basic shader pass interface -* @public */ -export interface BasePass { +export type BasePass ={ // child name name: string; @@ -27,5 +26,5 @@ export interface BasePass { setSize(width: number, height: number); - render(renderer: THREE.WebGLRenderer, writeBuffer: THREE.WebGLRenderTarget, readBuffer: THREE.WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean); + render(renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean); } diff --git a/src/three/pass/FullScreenHelper.ts b/src/three/pass/FullScreenHelper.ts index dfe7329..3351b16 100644 --- a/src/three/pass/FullScreenHelper.ts +++ b/src/three/pass/FullScreenHelper.ts @@ -4,7 +4,7 @@ * @author hexxone / https://hexx.one */ -import {BufferGeometry, Camera, Material, Mesh, OrthographicCamera, PlaneBufferGeometry, WebGLRenderer} from 'three'; +import {Camera, BufferGeometry, Mesh, Material, OrthographicCamera, PlaneBufferGeometry, WebGLRenderer} from '../../'; /** * Helper for passes that need to fill the viewport with a single quad. @@ -34,7 +34,7 @@ export class FullScreenHelper { * @param {WebGLRenderer} renderer */ public prepare(renderer: WebGLRenderer) { - renderer.compile(this.mesh, this.camera); + renderer.compile(this.mesh as any, this.camera); } /** diff --git a/src/three/pass/RenderPass.ts b/src/three/pass/RenderPass.ts index 8672e78..149b2ed 100644 --- a/src/three/pass/RenderPass.ts +++ b/src/three/pass/RenderPass.ts @@ -1,82 +1,103 @@ /** -* @author alteredq / http://alteredqualia.com/ -* -* @author hexxone / https://hexx.one -*/ - -import {Camera, Color, Material, Scene, WebGLRenderer} from 'three'; -import {BasePass} from './BasePass'; + * @author alteredq / http://alteredqualia.com/ + * + * @author hexxone / https://hexx.one + */ + +import { + Camera, + Color, + Material, + Scene, + WebGLRenderer, + WebGLRenderTarget, +} from "../../"; +import { BasePass } from "./BasePass"; /** -* Shader Render Helper -* @public -*/ + * Shader Render Helper + */ export class RenderPass implements BasePass { - name = 'RenderPass'; + name = "RenderPass"; enabled = true; needsSwap = true; clear = true; clearColor: Color = null; + clearAlpha: number = null; clearDepth = false; - private scene: Scene = null; - private camera: Camera = null; - private overMat: Material = null; + scene: Scene = null; + camera: Camera = null; + overMat: Material = null; /** - * Construct helper - * @param {Scene} scene - * @param {Camera} camera - * @param {Material} overMat - * @param {Color} clearColor - * @param {number} clearAlpha - */ - constructor(scene: Scene, camera: Camera, overMat: Material, clearColor?, clearAlpha?: number) { + * Construct helper + * @param {Scene} scene Scene + * @param {Camera} camera Camera + * @param {Material} overMat Override material + * @param {Color} clearColor Clear color + * @param {number} clearAlpha Clear alpha + */ + constructor( + scene: Scene, + camera: Camera, + overMat: Material, + clearColor?, + clearAlpha?: number + ) { this.scene = scene; this.camera = camera; this.overMat = overMat; this.clearColor = clearColor; - this.clearAlpha = (clearAlpha !== undefined) ? clearAlpha : 0; + this.clearAlpha = clearAlpha !== undefined ? clearAlpha : 0; } /** - * precompile shader - * @param {WebGLRenderer} renderer - */ + * precompile shader + * @param {WebGLRenderer} renderer Context + */ public prepare(renderer: WebGLRenderer) { renderer.compile(this.scene, this.camera); } /** - * Destroy shader - */ + * Destroy shader + */ public dispose() { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } /** - * Updated screen size - * @param {number} width X - * @param {number} height Y - */ - public setSize(width: number, height: number) { } + * Updated screen size + * @param {number} width X + * @param {number} height Y + */ + public setSize(width: number, height: number) { + return; + } /** - * Render Frame - * @param {WebGLRenderer} renderer Context - * @param {WebGLRenderTarget} writeBuffer Output - * @param {WebGLRenderTarget} readBuffer Input - * @param {boolean} maskActive filter - * @param {boolean} renderToScreen render to canvas OR buffer - * @param {Camera} camera (optional) - * @public - */ - public render(renderer: THREE.WebGLRenderer, writeBuffer: THREE.WebGLRenderTarget, readBuffer: THREE.WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean) { + * Render Frame + * @param {WebGLRenderer} renderer Context + * @param {WebGLRenderTarget} writeBuffer Output + * @param {WebGLRenderTarget} readBuffer Input + * @param {boolean} maskActive filter + * @param {boolean} renderToScreen render to canvas OR buffer + * @param {Camera} camera (optional) + * @public + */ + public render( + renderer: WebGLRenderer, + writeBuffer: WebGLRenderTarget, + readBuffer: WebGLRenderTarget, + maskActive: boolean, + renderToScreen: boolean + ) { const oldAutoClear = renderer.autoClear; renderer.autoClear = false; @@ -98,12 +119,19 @@ export class RenderPass implements BasePass { renderer.setRenderTarget(renderToScreen ? null : writeBuffer); // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 - if (this.clear) renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); + if (this.clear) + renderer.clear( + renderer.autoClearColor, + renderer.autoClearDepth, + renderer.autoClearStencil + ); + renderer.render(this.scene, this.camera); - if (this.clearColor) renderer.setClearColor(oldClearColor, oldClearAlpha); + + if (this.clearColor) + renderer.setClearColor(oldClearColor, oldClearAlpha); this.scene.overrideMaterial = null; renderer.autoClear = oldAutoClear; } } - diff --git a/src/three/pass/ShaderPass.ts b/src/three/pass/ShaderPass.ts index 06ba861..fd20926 100644 --- a/src/three/pass/ShaderPass.ts +++ b/src/three/pass/ShaderPass.ts @@ -1,19 +1,23 @@ /** -* @author alteredq / http://alteredqualia.com/ -* -* @author hexxone / https://hexx.one -*/ + * @author alteredq / http://alteredqualia.com/ + * + * @author hexxone / https://hexx.one + */ -import {ShaderMaterial, UniformsUtils, Vector2, WebGLRenderer, WebGLRenderTarget} from 'three'; -import {BaseShader} from '../shader/BaseShader'; - -import {FullScreenHelper} from './FullScreenHelper'; -import {BasePass} from './BasePass'; +import { + BasePass, + BaseShader, + FullScreenHelper, + ShaderMaterial, + UniformsUtils, + Vector2, + WebGLRenderer, + WebGLRenderTarget, +} from "../../"; /** -* ThreeJS Pass for easy full screen shaders -* @public -*/ + * ThreeJS Pass for easy full screen shaders + */ export class ShaderPass implements BasePass { name: string; enabled = true; @@ -26,68 +30,75 @@ export class ShaderPass implements BasePass { iRes: Vector2; /** - * Make Pass - * default Material will enable transparency! - * @param {BaseShader|ShaderMaterial} shader Create From - * @param {string} textureID Input Uniform Texture name - */ - constructor(shader: BaseShader | ShaderMaterial, textureID: string = 'tDiffuse') { + * Make Pass + * default Material will enable transparency! + * @param {BaseShader|ShaderMaterial} shader Create From + * @param {string} textureID Input Uniform Texture name + */ + constructor(shader: BaseShader | ShaderMaterial, textureID = "tDiffuse") { this.textureID = textureID; if (shader instanceof ShaderMaterial) { - this.name = 'ShaderMaterial'; + this.name = "ShaderMaterial"; this.uniforms = shader.uniforms; this.material = shader; } else if (shader) { this.name = shader.shaderID; this.uniforms = UniformsUtils.clone(shader.uniforms); - this.material = new ShaderMaterial({ - defines: Object.assign({}, shader.defines), - uniforms: this.uniforms, - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader, - }); + this.material = new ShaderMaterial(); + this.material.defines = Object.assign({}, shader.defines); + this.material.uniforms = this.uniforms; + this.material.vertexShader = shader.vertexShader; + this.material.fragmentShader = shader.fragmentShader; } this.material.transparent = true; this.fsQuad = new FullScreenHelper(this.material); } /** - * precompile shader - * @param {WebGLRenderer} renderer - */ - public prepare(renderer: WebGLRenderer) { + * precompile shader + * @param {WebGLRenderer} renderer renderer + * @returns {void} + */ + prepare(renderer: WebGLRenderer): void { this.fsQuad.prepare(renderer); } /** - * Destroy Pass - * @public - */ - public dispose() { + * Destroy Pass + * @public + * @returns {void} + */ + dispose() { this.fsQuad.dispose(); } /** - * Canvas size update - * @param {number} width X - * @param {number} height Y - * @public - */ - public setSize(width: number, height: number) { + * Canvas size update + * @param {number} width X + * @param {number} height Y + * @returns {void} + */ + setSize(width: number, height: number) { this.iRes = new Vector2(width, height); } /** - * Render frame with chaining-support - * @param {WebGLRenderer} renderer - * @param {WebGLRenderTarget} writeBuffer wB - * @param {WebGLRenderTarget} readBuffer rB - * @param {boolean} maskActive mA - * @param {boolean} renderToScreen render to canvas OR buffer - * @public - */ - public render(renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean) { + * Render frame with chaining-support + * @param {WebGLRenderer} renderer renderer + * @param {WebGLRenderTarget} writeBuffer wB + * @param {WebGLRenderTarget} readBuffer rB + * @param {boolean} maskActive mA + * @param {boolean} renderToScreen render to canvas OR buffer + * @returns {void} + */ + render( + renderer: WebGLRenderer, + writeBuffer: WebGLRenderTarget, + readBuffer: WebGLRenderTarget, + maskActive: boolean, + renderToScreen: boolean + ) { if (this.uniforms[this.textureID]) { this.uniforms[this.textureID].value = readBuffer.texture; } @@ -103,7 +114,12 @@ export class ShaderPass implements BasePass { } else { renderer.setRenderTarget(writeBuffer); // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/js/pull/15571#issuecomment-465669600 - if (this.clear) renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); + if (this.clear) + renderer.clear( + renderer.autoClearColor, + renderer.autoClearDepth, + renderer.autoClearStencil + ); } this.fsQuad.render(renderer); } diff --git a/src/three/pass/UnrealBloomPass.ts b/src/three/pass/UnrealBloomPass.ts index 348d0f5..6219e3a 100644 --- a/src/three/pass/UnrealBloomPass.ts +++ b/src/three/pass/UnrealBloomPass.ts @@ -1,35 +1,44 @@ /** -* @author spidersharma / http://eduperiment.com/ -*/ - -import {AdditiveBlending, Color, LinearFilter, MeshBasicMaterial, RGBAFormat, ShaderMaterial, UniformsUtils, Vector2, Vector3, WebGLRenderer, WebGLRenderTarget} from 'three'; -import {FullScreenHelper} from './FullScreenHelper'; -import {BasePass} from './BasePass'; - -import {CopyShader} from '../shader/CopyShader'; -import {LuminosityHighPassShader} from '../shader/LuminosityHighPassShader'; + * @author spidersharma / http://eduperiment.com/ + */ + +import { + AdditiveBlending, + BasePass, + Color, + CopyShader, + FullScreenHelper, + LinearFilter, + LuminosityHighPassShader, + MeshBasicMaterial, + RGBAFormat, + ShaderMaterial, + UniformsUtils, + Vector2, + Vector3, + WebGLRenderer, + WebGLRenderTarget, +} from "../../"; /** -* Inspired from Unreal Engine -* https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/ -* -* @public -*/ + * Inspired from Unreal Engine + * https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/ + */ export class UnrealBloomPass implements BasePass { - public name = 'UnrealBloom'; + public name = "UnrealBloom"; public enabled = true; public needsSwap = false; public clear = true; - public oldClearColor = new Color(); + public oldClearColor = new Color(0); public oldClearAlpha = 1; - public clearColor = new Color(0, 0, 0); + public clearColor = new Color(0); private resolution = null; private strength = null; private radius = null; private threshold = null; - private renderTargetBright = null; + private renderTargetBright: WebGLRenderTarget; private highPassUniforms = null; // create color only once here, reuse it later inside the render function @@ -52,36 +61,52 @@ export class UnrealBloomPass implements BasePass { private blurDirY = new Vector2(0.0, 1.0); /** - * Construct bloom shader - * @param {Vector2} resolution size - * @param {number} strength multiplier - * @param {number} radius size - * @param {number} threshold min val - */ - constructor(resolution: Vector2, strength: number, radius: number, threshold: number) { - this.resolution = (resolution) ? resolution : new Vector2(256, 256); - this.strength = (strength !== undefined) ? strength : 1; + * Construct bloom shader + * @param {Vector2} resolution size + * @param {number} strength multiplier + * @param {number} radius size + * @param {number} threshold min val + */ + constructor( + resolution: Vector2, + strength: number, + radius: number, + threshold: number + ) { + this.resolution = resolution ? resolution : new Vector2(256, 256); + this.strength = strength !== undefined ? strength : 1; this.radius = radius; this.threshold = threshold; - // render targets - const pars = {minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat}; + const pars = { + minFilter: LinearFilter, + magFilter: LinearFilter, + format: RGBAFormat, + }; let resx = Math.round(this.resolution.x / 2); let resy = Math.round(this.resolution.y / 2); this.renderTargetBright = new WebGLRenderTarget(resx, resy, pars); - this.renderTargetBright.texture.name = 'UnrealBloomPass.bright'; + this.renderTargetBright.texture.name = "UnrealBloomPass.bright"; this.renderTargetBright.texture.generateMipmaps = false; for (let i = 0; i < this.nMips; i++) { - const renderTargetHorizonal = new WebGLRenderTarget(resx, resy, pars); - renderTargetHorizonal.texture.name = 'UnrealBloomPass.h' + i; + const renderTargetHorizonal = new WebGLRenderTarget( + resx, + resy, + pars + ); + renderTargetHorizonal.texture.name = "UnrealBloomPass.h" + i; renderTargetHorizonal.texture.generateMipmaps = false; this.renderTargetsHorizontal.push(renderTargetHorizonal); - const renderTargetVertical = new WebGLRenderTarget(resx, resy, pars); - renderTargetVertical.texture.name = 'UnrealBloomPass.v' + i; + const renderTargetVertical = new WebGLRenderTarget( + resx, + resy, + pars + ); + renderTargetVertical.texture.name = "UnrealBloomPass.v" + i; renderTargetVertical.texture.generateMipmaps = false; this.renderTargetsVertical.push(renderTargetVertical); @@ -93,15 +118,15 @@ export class UnrealBloomPass implements BasePass { const highPassShader = new LuminosityHighPassShader(); this.highPassUniforms = UniformsUtils.clone(highPassShader.uniforms); - this.highPassUniforms['luminosityThreshold'].value = threshold; - this.highPassUniforms['smoothWidth'].value = 0.01; + this.highPassUniforms["luminosityThreshold"].value = threshold; + this.highPassUniforms["smoothWidth"].value = 0.01; - this.materialHighPassFilter = new ShaderMaterial({ - uniforms: this.highPassUniforms, - vertexShader: highPassShader.vertexShader, - fragmentShader: highPassShader.fragmentShader, - defines: {}, - }); + this.materialHighPassFilter = new ShaderMaterial(); + this.materialHighPassFilter.uniforms = this.highPassUniforms; + this.materialHighPassFilter.vertexShader = highPassShader.vertexShader; + this.materialHighPassFilter.fragmentShader = + highPassShader.fragmentShader; + this.materialHighPassFilter.defines = {}; // Gaussian Blur Materials const kernelSizeArray = [3, 5, 7, 9, 11]; @@ -109,8 +134,11 @@ export class UnrealBloomPass implements BasePass { resy = Math.round(this.resolution.y / 2); for (let i = 0; i < this.nMips; i++) { - this.separableBlurMaterials.push(this.getSeperableBlurMaterial(kernelSizeArray[i])); - this.separableBlurMaterials[i].uniforms['texSize'].value = new Vector2(resx, resy); + this.separableBlurMaterials.push( + this.getSeperableBlurMaterial(kernelSizeArray[i]) + ); + this.separableBlurMaterials[i].uniforms["texSize"].value = + new Vector2(resx, resy); resx = Math.round(resx / 2); resy = Math.round(resy / 2); @@ -118,50 +146,63 @@ export class UnrealBloomPass implements BasePass { // Composite material this.compositeMaterial = this.getCompositeMaterial(this.nMips); - this.compositeMaterial.uniforms['blurTexture1'].value = this.renderTargetsVertical[0].texture; - this.compositeMaterial.uniforms['blurTexture2'].value = this.renderTargetsVertical[1].texture; - this.compositeMaterial.uniforms['blurTexture3'].value = this.renderTargetsVertical[2].texture; - this.compositeMaterial.uniforms['blurTexture4'].value = this.renderTargetsVertical[3].texture; - this.compositeMaterial.uniforms['blurTexture5'].value = this.renderTargetsVertical[4].texture; - this.compositeMaterial.uniforms['bloomStrength'].value = strength; - this.compositeMaterial.uniforms['bloomRadius'].value = 0.1; + this.compositeMaterial.uniforms["blurTexture1"].value = + this.renderTargetsVertical[0].texture; + this.compositeMaterial.uniforms["blurTexture2"].value = + this.renderTargetsVertical[1].texture; + this.compositeMaterial.uniforms["blurTexture3"].value = + this.renderTargetsVertical[2].texture; + this.compositeMaterial.uniforms["blurTexture4"].value = + this.renderTargetsVertical[3].texture; + this.compositeMaterial.uniforms["blurTexture5"].value = + this.renderTargetsVertical[4].texture; + this.compositeMaterial.uniforms["bloomStrength"].value = strength; + this.compositeMaterial.uniforms["bloomRadius"].value = 0.1; this.compositeMaterial.needsUpdate = true; const bloomFactors = [1.0, 0.8, 0.6, 0.4, 0.2]; - this.compositeMaterial.uniforms['bloomFactors'].value = bloomFactors; - this.bloomTintColors = [new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1)]; - this.compositeMaterial.uniforms['bloomTintColors'].value = this.bloomTintColors; + this.compositeMaterial.uniforms["bloomFactors"].value = bloomFactors; + this.bloomTintColors = [ + new Vector3(1, 1, 1), + new Vector3(1, 1, 1), + new Vector3(1, 1, 1), + new Vector3(1, 1, 1), + new Vector3(1, 1, 1), + ]; + this.compositeMaterial.uniforms["bloomTintColors"].value = + this.bloomTintColors; // copy material const copyShader = new CopyShader(); this.copyUniforms = UniformsUtils.clone(copyShader.uniforms); - this.copyUniforms['opacity'].value = 1.0; - - this.materialCopy = new ShaderMaterial({ - uniforms: this.copyUniforms, - vertexShader: copyShader.vertexShader, - fragmentShader: copyShader.fragmentShader, - blending: AdditiveBlending, - depthTest: false, - depthWrite: false, - transparent: true, - }); + this.copyUniforms["opacity"].value = 1.0; + + this.materialCopy = new ShaderMaterial(); + this.materialCopy.uniforms = this.copyUniforms; + this.materialCopy.vertexShader = copyShader.vertexShader; + this.materialCopy.fragmentShader = copyShader.fragmentShader; + this.materialCopy.blending = AdditiveBlending; + this.materialCopy.depthTest = false; + this.materialCopy.depthWrite = false; + this.materialCopy.transparent = true; } /** - * precompile shader - * @param {WebGLRenderer} renderer - */ - public prepare(renderer: WebGLRenderer) { + * precompile shader + * @param {WebGLRenderer} renderer renderer + * @returns {void} + */ + prepare(renderer: WebGLRenderer) { this.fsQuad.prepare(renderer); } /** - * Destroy shader - */ - public dispose() { + * Destroy shader + * @returns {void} + */ + dispose() { for (let i = 0; i < this.renderTargetsHorizontal.length; i++) { this.renderTargetsHorizontal[i].dispose(); } @@ -173,11 +214,12 @@ export class UnrealBloomPass implements BasePass { } /** - * Updated screen size - * @param {number} width X - * @param {number} height Y - */ - public setSize(width: number, height: number) { + * Updated screen size + * @param {number} width X + * @param {number} height Y + * @returns {void} + */ + setSize(width: number, height: number) { let resx = Math.round(width / 2); let resy = Math.round(height / 2); this.renderTargetBright.setSize(resx, resy); @@ -186,7 +228,8 @@ export class UnrealBloomPass implements BasePass { this.renderTargetsHorizontal[i].setSize(resx, resy); this.renderTargetsVertical[i].setSize(resx, resy); - this.separableBlurMaterials[i].uniforms['texSize'].value = new Vector2(resx, resy); + this.separableBlurMaterials[i].uniforms["texSize"].value = + new Vector2(resx, resy); resx = Math.round(resx / 2); resy = Math.round(resy / 2); @@ -194,15 +237,21 @@ export class UnrealBloomPass implements BasePass { } /** - * Render Frame - * @param {WebGLRenderer} renderer Context - * @param {WebGLRenderTarget} writeBuffer Output - * @param {WebGLRenderTarget} readBuffer Input - * @param {boolean} maskActive filter - * @param {boolean} renderToScreen render to canvas OR buffer - * @public - */ - public render(renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean) { + * Render Frame + * @param {WebGLRenderer} renderer Context + * @param {WebGLRenderTarget} writeBuffer Output + * @param {WebGLRenderTarget} readBuffer Input + * @param {boolean} maskActive filter + * @param {boolean} renderToScreen render to canvas OR buffer + * @returns {void} + */ + render( + renderer: WebGLRenderer, + writeBuffer: WebGLRenderTarget, + readBuffer: WebGLRenderTarget, + maskActive: boolean, + renderToScreen: boolean + ) { renderer.getClearColor(this.oldClearColor); this.oldClearAlpha = renderer.getClearAlpha(); const oldAutoClear = renderer.autoClear; @@ -223,8 +272,8 @@ export class UnrealBloomPass implements BasePass { // 1. Extract Bright Areas - this.highPassUniforms['tDiffuse'].value = readBuffer.texture; - this.highPassUniforms['luminosityThreshold'].value = this.threshold; + this.highPassUniforms["tDiffuse"].value = readBuffer.texture; + this.highPassUniforms["luminosityThreshold"].value = this.threshold; this.fsQuad.setMaterial(this.materialHighPassFilter); renderer.setRenderTarget(this.renderTargetBright); @@ -238,14 +287,18 @@ export class UnrealBloomPass implements BasePass { for (let i = 0; i < this.nMips; i++) { this.fsQuad.setMaterial(this.separableBlurMaterials[i]); - this.separableBlurMaterials[i].uniforms['colorTexture'].value = inputRenderTarget.texture; - this.separableBlurMaterials[i].uniforms['direction'].value = this.blurDirX; + this.separableBlurMaterials[i].uniforms["colorTexture"].value = + inputRenderTarget.texture; + this.separableBlurMaterials[i].uniforms["direction"].value = + this.blurDirX; renderer.setRenderTarget(this.renderTargetsHorizontal[i]); renderer.clear(); this.fsQuad.render(renderer); - this.separableBlurMaterials[i].uniforms['colorTexture'].value = this.renderTargetsHorizontal[i].texture; - this.separableBlurMaterials[i].uniforms['direction'].value = this.blurDirY; + this.separableBlurMaterials[i].uniforms["colorTexture"].value = + this.renderTargetsHorizontal[i].texture; + this.separableBlurMaterials[i].uniforms["direction"].value = + this.blurDirY; renderer.setRenderTarget(this.renderTargetsVertical[i]); renderer.clear(); this.fsQuad.render(renderer); @@ -256,9 +309,10 @@ export class UnrealBloomPass implements BasePass { // Composite All the mips this.fsQuad.setMaterial(this.compositeMaterial); - this.compositeMaterial.uniforms['bloomStrength'].value = this.strength; - this.compositeMaterial.uniforms['bloomRadius'].value = this.radius; - this.compositeMaterial.uniforms['bloomTintColors'].value = this.bloomTintColors; + this.compositeMaterial.uniforms["bloomStrength"].value = this.strength; + this.compositeMaterial.uniforms["bloomRadius"].value = this.radius; + this.compositeMaterial.uniforms["bloomTintColors"].value = + this.bloomTintColors; renderer.setRenderTarget(this.renderTargetsHorizontal[0]); renderer.clear(); @@ -267,7 +321,8 @@ export class UnrealBloomPass implements BasePass { // Blend it additively over the input texture this.fsQuad.setMaterial(this.materialCopy); - this.copyUniforms['tDiffuse'].value = this.renderTargetsHorizontal[0].texture; + this.copyUniforms["tDiffuse"].value = + this.renderTargetsHorizontal[0].texture; if (maskActive) renderer.context.enable(renderer.context.STENCIL_TEST); @@ -280,33 +335,32 @@ export class UnrealBloomPass implements BasePass { } /** - * Make seperable material - * @param {number} kernelRadius size - * @return {ShaderMaterial} - */ + * Make seperable material + * @param {number} kernelRadius size + * @return {ShaderMaterial} material + */ private getSeperableBlurMaterial(kernelRadius) { - return new ShaderMaterial({ + const sm = new ShaderMaterial(); - defines: { - 'KERNEL_RADIUS': kernelRadius, - 'SIGMA': kernelRadius, - }, + sm.defines = { + "KERNEL_RADIUS": kernelRadius, + "SIGMA": kernelRadius, + }; - uniforms: { - 'colorTexture': {value: null}, - 'texSize': {value: new Vector2(0.5, 0.5)}, - 'direction': {value: new Vector2(0.5, 0.5)}, - }, + sm.uniforms = { + "colorTexture": { value: null }, + "texSize": { value: new Vector2(0.5, 0.5) }, + "direction": { value: new Vector2(0.5, 0.5) }, + }; - vertexShader: ` + sm.vertexShader = ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - }`, + }`; - fragmentShader: - `#include + sm.fragmentShader = `#include varying vec2 vUv; uniform sampler2D colorTexture; @@ -333,68 +387,69 @@ export class UnrealBloomPass implements BasePass { weightSum += 2.0 * w; } gl_FragColor = vec4(diffuseSum/weightSum, alphaSum/weightSum); - }`, - }); + }`; + + return sm; } /** - * Make helper material - * @param {number} nMips MipMaps - * @return {ShaderMaterial} - */ + * Make helper material + * @param {number} nMips MipMaps + * @return {ShaderMaterial} material + */ private getCompositeMaterial(nMips) { - return new ShaderMaterial({ - - defines: { - 'NUM_MIPS': nMips, - }, - - uniforms: { - 'blurTexture1': {value: null}, - 'blurTexture2': {value: null}, - 'blurTexture3': {value: null}, - 'blurTexture4': {value: null}, - 'blurTexture5': {value: null}, - 'dirtTexture': {value: null}, - 'bloomStrength': {value: 1.0}, - 'bloomFactors': {value: null}, - 'bloomTintColors': {value: null}, - 'bloomRadius': {value: 0.0}, - }, - - vertexShader: ` - varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - }`, + const sm = new ShaderMaterial(); + sm.defines = { + "NUM_MIPS": nMips, + }; + + sm.uniforms = { + "blurTexture1": { value: null }, + "blurTexture2": { value: null }, + "blurTexture3": { value: null }, + "blurTexture4": { value: null }, + "blurTexture5": { value: null }, + "dirtTexture": { value: null }, + "bloomStrength": { value: 1.0 }, + "bloomFactors": { value: null }, + "bloomTintColors": { value: null }, + "bloomRadius": { value: 0.0 }, + }; + + sm.vertexShader = ` +varying vec2 vUv; +void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); +}`; + + sm.fragmentShader = ` +varying vec2 vUv; + +uniform sampler2D blurTexture1; +uniform sampler2D blurTexture2; +uniform sampler2D blurTexture3; +uniform sampler2D blurTexture4; +uniform sampler2D blurTexture5; +uniform sampler2D dirtTexture; +uniform float bloomStrength; +uniform float bloomRadius; +uniform float bloomFactors[NUM_MIPS]; +uniform vec3 bloomTintColors[NUM_MIPS]; + +float lerpBloomFactor(const in float factor) { + float mirrorFactor = 1.2 - factor; + return mix(factor, mirrorFactor, bloomRadius); +} - fragmentShader: ` - varying vec2 vUv; - - uniform sampler2D blurTexture1; - uniform sampler2D blurTexture2; - uniform sampler2D blurTexture3; - uniform sampler2D blurTexture4; - uniform sampler2D blurTexture5; - uniform sampler2D dirtTexture; - uniform float bloomStrength; - uniform float bloomRadius; - uniform float bloomFactors[NUM_MIPS]; - uniform vec3 bloomTintColors[NUM_MIPS]; - - float lerpBloomFactor(const in float factor) { - float mirrorFactor = 1.2 - factor; - return mix(factor, mirrorFactor, bloomRadius); - } - - void main() { - gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + - lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + - lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + - lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + - lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) ); - }`, - }); +void main() { + gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + + lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + + lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + + lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + + lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) ); +}`; + + return sm; } } diff --git a/src/three/shader/BlendShader.ts b/src/three/shader/BlendShader.ts index 809c98e..45e4f76 100644 --- a/src/three/shader/BlendShader.ts +++ b/src/three/shader/BlendShader.ts @@ -23,9 +23,9 @@ export class BlendShader implements BaseShader { shaderID = 'blendShader'; uniforms = { - tDiffuse: {value: null}, - overlayBuffer: {value: null}, - mixValue: {value: 1}, + "tDiffuse": {value: null}, + "overlayBuffer": {value: null}, + "mixValue": {value: 1}, }; vertexShader = vertex; diff --git a/src/three/shader/BlurShader.ts b/src/three/shader/BlurShader.ts index c4f451f..3c628e4 100644 --- a/src/three/shader/BlurShader.ts +++ b/src/three/shader/BlurShader.ts @@ -7,8 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import {Vector2} from 'three'; -import {BaseShader} from './BaseShader'; +import {BaseShader, Vector2} from '../..'; import vertex from './vertex/Basic.glsl'; import fragment from './fragment/Blur.glsl'; @@ -24,10 +23,10 @@ export class BlurShader implements BaseShader { shaderID = 'blurShader'; uniforms = { - tDiffuse: {value: null}, - iResolution: {value: new Vector2(1, 1)}, - u_sigma: {value: 0.5}, - u_dir: {value: new Vector2(0.1, 0.1)}, + "tDiffuse": {value: null}, + "iResolution": {value: new Vector2(1, 1)}, + "u_sigma": {value: 0.5}, + "u_dir": {value: new Vector2(0.1, 0.1)}, }; vertexShader = vertex; diff --git a/src/three/shader/ChromaticShader.ts b/src/three/shader/ChromaticShader.ts index c0ccf97..0d4e59d 100644 --- a/src/three/shader/ChromaticShader.ts +++ b/src/three/shader/ChromaticShader.ts @@ -7,7 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import {Vector2} from 'three'; +import {Vector2} from '../../'; import {BaseShader} from './BaseShader'; import vertex from './vertex/Basic.glsl'; @@ -24,9 +24,9 @@ export class ChromaticShader implements BaseShader { shaderID = 'chromaticShader'; uniforms = { - tDiffuse: {value: null}, - iResolution: {value: new Vector2(1, 1)}, - strength: {value: 10.0}, + "tDiffuse": {value: null}, + "iResolution": {value: new Vector2(1, 1)}, + "strength": {value: 10.0}, }; vertexShader = vertex; diff --git a/src/three/shader/CopyShader.ts b/src/three/shader/CopyShader.ts index 4629c06..0c64908 100644 --- a/src/three/shader/CopyShader.ts +++ b/src/three/shader/CopyShader.ts @@ -24,9 +24,9 @@ export class CopyShader implements BaseShader { shaderID = 'copyShader'; uniforms = { - tDiffuse: {value: null}, - opacity: {value: 1.0}, - } + "tDiffuse": {value: null}, + "opacity": {value: 1.0}, + }; vertexShader = vertex; diff --git a/src/three/shader/FXAAShader.ts b/src/three/shader/FXAAShader.ts index 1bc269f..419aabc 100644 --- a/src/three/shader/FXAAShader.ts +++ b/src/three/shader/FXAAShader.ts @@ -4,8 +4,7 @@ * @author hexxone / https://hexx.one */ -import {Vector2} from 'three'; -import {BaseShader} from './BaseShader'; +import {Vector2, BaseShader} from '../../'; import vertex from './vertex/Basic.glsl'; import fragment from './fragment/FXAA.glsl'; @@ -25,9 +24,9 @@ export class FXAAShader implements BaseShader { shaderID = 'fxaaShader'; uniforms = { - tDiffuse: {value: null}, - resolution: {value: new Vector2(1 / 1024, 1 / 512)}, - } + "tDiffuse": {value: null}, + "resolution": {value: new Vector2(1 / 1024, 1 / 512)}, + }; vertexShader = vertex; diff --git a/src/three/shader/FractalMirrorShader.ts b/src/three/shader/FractalMirrorShader.ts index ec63716..db5e2b9 100644 --- a/src/three/shader/FractalMirrorShader.ts +++ b/src/three/shader/FractalMirrorShader.ts @@ -9,8 +9,7 @@ * @description */ -import {Vector2} from 'three'; -import {BaseShader} from './BaseShader'; +import {Vector2, BaseShader} from '../../'; import vertex from './vertex/Basic.glsl'; import fragment from './fragment/FractalMirror.glsl'; @@ -28,10 +27,10 @@ export class FractalMirrorShader implements BaseShader { shaderID = 'fractalMirror'; uniforms = { - tDiffuse: {value: null}, - iResolution: {value: new Vector2(16, 9)}, - numSides: {value: 2.0}, // minimum value - invert: {value: false}, + "tDiffuse": {value: null}, + "iResolution": {value: new Vector2(16, 9)}, + "numSides": {value: 2.0}, // minimum value + "invert": {value: false}, }; vertexShader = vertex; diff --git a/src/three/shader/LUTShader.ts b/src/three/shader/LUTShader.ts index 95fe15f..11e5ce4 100644 --- a/src/three/shader/LUTShader.ts +++ b/src/three/shader/LUTShader.ts @@ -25,9 +25,9 @@ export class LUTShader implements BaseShader { shaderID = 'LUTShader'; uniforms = { - tDiffuse: {value: null}, - lutMap: {value: null}, - lutMapSize: {value: 1}, + "tDiffuse": {value: null}, + "lutMap": {value: null}, + "lutMapSize": {value: 1}, }; vertexShader = vertex; diff --git a/src/three/shader/LuminosityHighPassShader.ts b/src/three/shader/LuminosityHighPassShader.ts index 6b06413..8c42f57 100644 --- a/src/three/shader/LuminosityHighPassShader.ts +++ b/src/three/shader/LuminosityHighPassShader.ts @@ -8,8 +8,7 @@ * See LICENSE file in the project root for full license information. */ -import {Color} from 'three'; -import {BaseShader} from './BaseShader'; +import {Color, BaseShader} from '../../'; import vertex from './vertex/Basic.glsl'; import fragment from './fragment/Luminosity.glsl'; @@ -27,11 +26,11 @@ export class LuminosityHighPassShader implements BaseShader { shaderID = 'luminosityHighPass'; uniforms = { - tDiffuse: {value: null}, - luminosityThreshold: {value: 1.0}, - smoothWidth: {value: 1.0}, - defaultColor: {value: new Color(0x000000)}, // @TODO might need to set to BG color? - defaultOpacity: {value: 0.0}, + "tDiffuse": {value: null}, + "luminosityThreshold": {value: 1.0}, + "smoothWidth": {value: 1.0}, + "defaultColor": {value: new Color(0x000000)}, // @TODO might need to set to BG color? + "defaultOpacity": {value: 0.0}, }; vertexShader = vertex; diff --git a/src/wasc-worker b/src/wasc-worker index e8c5719..176de5f 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit e8c5719b9c23f6bc53bf23f1c1f303cf0d52df03 +Subproject commit 176de5f1fbbdb2fa4963966196ae8cae5a6357c1 diff --git a/src/weas/Bea.ts b/src/weas/Bea.ts index 4dc241a..ab6d59d 100644 --- a/src/weas/Bea.ts +++ b/src/weas/Bea.ts @@ -34,18 +34,18 @@ * @public */ export class Bea_ts { - bd_low: BeatsDetektor = new BeatsDetektor(55, 109); - bd_med: BeatsDetektor = new BeatsDetektor(85, 169); - bd_high: BeatsDetektor = new BeatsDetektor(100, 199); - bd_ext: BeatsDetektor = new BeatsDetektor(150, 299); + private bd_low: BeatsDetektor = new BeatsDetektor(55, 109); + private bd_med: BeatsDetektor = new BeatsDetektor(85, 169); + private bd_high: BeatsDetektor = new BeatsDetektor(100, 199); + private bd_ext: BeatsDetektor = new BeatsDetektor(150, 299); - winRewards: number[] = []; + private winRewards: number[] = []; - bds: BeatsDetektor[] = []; - bdi = 0; + private bds: BeatsDetektor[] = []; + private bdi = 0; - prev_bpm: number = -1; - prev_win: number = -1; + private prev_bpm: number = -1; + private prev_win: number = -1; /** * Construct BeatDetektor array diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 68604fd..bdcfe6c 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -7,12 +7,8 @@ * See LICENSE file in the project root for full license information. */ -import {CComponent} from '../CComponent'; -import {CSettings} from '../CSettings'; -import {waitReady} from '../Util'; -import {Smallog} from '../Smallog'; +import {CComponent, CSettings, Smallog, waitReady, WascInterface, wascWorker, WascLoader, ASUtil} from '../'; -import {wascWorker, WascInterface} from '../wasc-worker'; import {Bea_ts} from './Bea'; const DAT_LEN = 128; @@ -24,39 +20,39 @@ const LISTENAME = 'wallpaperRegisterAudioListener'; * @extends {CSettings} */ export class WEASettings extends CSettings { - debugging: boolean = false; + debugging = false; /** do audio processing? */ - audioprocessing: boolean = true; + audioprocessing = true; // do dynamic processing? - equalize: boolean = true; + equalize = true; // convert to mono? - mono_audio: boolean = true; + mono_audio = true; // invert low & high freqs? - audio_direction: number = 0; + audio_direction = 0; // peak filtering - peak_filter: number = 1; + peak_filter = 1; // neighbour-smoothing value - value_smoothing: number = 2; + value_smoothing = 2; // time-value smoothing ratio - audio_increase: number = 75; - audio_decrease: number = 25; + audio_increase = 75; + audio_decrease = 25; // multipliers - treble_multiplier: number = 0.666; - mids_multiplier: number = 0.8; - bass_multiplier: number = 1.2; + treble_multiplier = 0.666; + mids_multiplier = 0.8; + bass_multiplier = 1.2; // ignore value leveling for "silent" data - minimum_volume: number = 0.005; + minimum_volume = 0.005; // use low latency audio? - low_latency: boolean = false; + low_latency = false; // show debug rendering? - show_canvas: boolean = true; + show_canvas = true; } /** * Processed audio data representation * @public */ -export interface WEAudio { +export type WEAudio = { time: number; ellapsed: number; data: Float64Array; @@ -136,27 +132,32 @@ export class WEAS extends CComponent { public inBuff = new Float64Array(DAT_LEN); // web assembly functions - private weasModule: WascInterface = null; + weasModule: WascInterface = null; // bpm detektor - private bpModule: Bea_ts; + bpModule: Bea_ts; // debug render elements - private mainElm: HTMLDivElement; - private canvas1: HTMLCanvasElement; - private context1: CanvasRenderingContext2D; - private canvas2: HTMLCanvasElement; - private context2: CanvasRenderingContext2D; - private display: HTMLElement; + mainElm: HTMLDivElement; + canvas1: HTMLCanvasElement; + context1: CanvasRenderingContext2D; + canvas2: HTMLCanvasElement; + context2: CanvasRenderingContext2D; + display: HTMLElement; + + sharedASUtils: ASUtil; + + public init?: () => Promise; /** * delay audio initialization until page ready * @param {boolean} detectBpm (optional) */ - constructor(detectBpm = false) { + constructor(autoInit = false, detectBpm = false) { super(); if (detectBpm) this.bpModule = new Bea_ts(); - waitReady().then(() => this.realInit()); + if (autoInit) waitReady().then(() => this.realInit()); + else this.init = this.realInit; } @@ -185,16 +186,18 @@ export class WEAS extends CComponent { Smallog.error('\'window.wallpaperRegisterAudioListener\' not given!'); return; } + this.init = null; this.injectCSS(); this.injectHTML(); - wascWorker('WEAS.wasm', 4096, {}, !this.settings.low_latency).then((mod) => { - this.weasModule = mod; + const iso = window['crossOriginIsolated'] === true; + wascWorker('WEAS.wasm', 4096, iso, {}, !this.settings.low_latency).then((myModule) => { + this.weasModule = myModule; - if (mod.memoryBuffer) { - // @todo - // console.log('Yeah Boiii, we got shared memory access to WebAssembly worker!'); + if (myModule.sharedMemory) { + this.sharedASUtils = new WascLoader().postInstantiate({}, {exports: {memory: myModule.sharedMemory}} as any); + Smallog.debug('Got shared memory access to WebAssembly audio!'); } this.updateSettings().then(() => { @@ -397,7 +400,7 @@ export class WEAS extends CComponent { { data: sett.buffer, }) - // Back to main context + // Back to main context .then(() => { Smallog.debug('Sent Settings to WEAS: ' + JSON.stringify(sett)); resolve(); @@ -421,8 +424,8 @@ export class WEAS extends CComponent { if (this.settings.show_canvas) this.updateCanvas(); return this.settings.audioprocessing && - this.lastAudio && this.lastAudio.silent == 0 && - (performance.now() - this.lastAudio.time < 3000); + this.lastAudio && this.lastAudio.silent == 0 && + (performance.now() - this.lastAudio.time < 3000); } /** From bfd29e655ab1853a5063344c9f55cec67a7ad4c9 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Thu, 3 Mar 2022 12:52:13 +0100 Subject: [PATCH 48/76] formatting with prettier --- src/CComponent.ts | 82 +-- src/CSettings.ts | 68 ++- src/FPSta.ts | 158 +++--- src/ICUE.d.ts | 334 +++++++----- src/LoadHelper.ts | 87 +-- src/ReloadHelper.ts | 100 ++-- src/Smallog.ts | 117 ++-- src/Util.ts | 123 +++-- src/WEICUE.ts | 326 +++++++----- src/WEWA.ts | 510 +++++++++--------- src/WarnHelper.ts | 107 ++-- src/XRHelper.ts | 213 ++++---- src/XRWebGL.d.ts | 530 +++++++++++-------- src/gles.d.ts | 213 ++++++-- src/glsl.d.ts | 2 +- src/index.ts | 33 +- src/renamer/RenamerPlugin.js | 286 ++++++---- src/three.ts | 2 +- src/three/pass/ShaderPass.ts | 77 ++- src/three/shader/fragment/FractalMirror.glsl | 9 +- src/wasc-worker | 2 +- src/weas/Bea.ts | 477 ++++++++++------- src/weas/WEAS.ts | 379 +++++++------ src/weas/WEAS.wasm.asc | 10 +- src/weas/index.ts | 20 +- src/worker-loader.d.ts | 37 +- 26 files changed, 2464 insertions(+), 1838 deletions(-) diff --git a/src/CComponent.ts b/src/CComponent.ts index eeb81b2..3dbfbc5 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -1,58 +1,60 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ -import {CSettings, Smallog} from './'; +import { CSettings, Smallog } from "./"; /** -* Base-Component for a TypeScript Web Wallpaper -*/ + * Base-Component for a TypeScript Web Wallpaper + */ export class CComponent { private needsUpdate = false; /** - * Important: Append your child objects, for settings to be applied correctly! - */ + * Important: Append your child objects, for settings to be applied correctly! + */ children: CComponent[] = []; /** - * main Settings, need to be overwritten with Specific settings - * @public - */ + * main Settings, need to be overwritten with Specific settings + * @public + */ public settings: CSettings = null; /** - * will recursively try to set a setting with type and return success - *
      - * will also flag the module as needs-Update. - * - * @public - * @param {Object} key - * @param {Object} value - * @return {boolean} found - */ + * will recursively try to set a setting with type and return success + *
      + * will also flag the module as needs-Update. + * + * @public + * @param {Object} key + * @param {Object} value + * @return {boolean} found + */ public applySetting(key: any, value: any): boolean { let found = this.settings.apply(key, value); if (found) { this.needsUpdate = true; Smallog.debug(`ApplySetting: ${key}:${value}`); } - this.children.forEach((ch) => found = ch.applySetting(key, value) || found); + this.children.forEach( + (ch) => (found = ch.applySetting(key, value) || found) + ); return found; } /** - * DO NOT OVERWWRITE !!! - *
      - * will recursively update all needed modules after settings changes - * - * @public - */ + * DO NOT OVERWWRITE !!! + *
      + * will recursively update all needed modules after settings changes + * + * @public + */ public updateAll(): void { this.children.forEach((c) => c.updateAll()); if (this.needsUpdate) this.updateSettings(); @@ -60,15 +62,17 @@ export class CComponent { } /** - * NEEDS TO BE OVERWRITTEN FOR DOING ACTIONS ON SETTINGS CHANGE. - *
      - * should usually get called automatically when needed.. no need for extra calling - * - * @public - * @return {Promise} async commpletion event - */ + * NEEDS TO BE OVERWRITTEN FOR DOING ACTIONS ON SETTINGS CHANGE. + *
      + * should usually get called automatically when needed.. no need for extra calling + * + * @public + * @return {Promise} async commpletion event + */ public updateSettings(): Promise { - console.error(`ERROR_NO_IMPL at: CComponent.UpdateSettings!\r\nPlease override this method!`); + console.error( + `ERROR_NO_IMPL at: CComponent.UpdateSettings!\r\nPlease override this method!` + ); return Promise.resolve(); } } diff --git a/src/CSettings.ts b/src/CSettings.ts index 9fd2543..0ecbde3 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -1,45 +1,55 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ -import {Smallog} from './'; +import { Smallog } from "./"; /** -* Base-Component Settings helper -* -* Core Settings interface, used for type-secure setting applying. -* -* All Settings-classes should be dereived from this one. -* -* @public -* @see {CComponent} -*/ + * Base-Component Settings helper + * + * Core Settings interface, used for type-secure setting applying. + * + * All Settings-classes should be dereived from this one. + * + * @public + * @see {CComponent} + */ export class CSettings { /** - * check if a certain key exists on a (dereived) object. - * if it exists, the value type matches and the value is not already equal, apply & return true - * otherwise return false - * - * @public - * @param {string} key - * @param {Object} castedValue - * @return {boolean} value was found & changed - */ + * check if a certain key exists on a (dereived) object. + * if it exists, the value type matches and the value is not already equal, apply & return true + * otherwise return false + * + * @public + * @param {string} key + * @param {Object} castedValue + * @return {boolean} value was found & changed + */ public apply(key: string, castedValue: any): boolean { if (this[key] !== undefined) { if (typeof this[key] === typeof castedValue) { if (this[key] !== castedValue) { this[key] = castedValue; return true; - } else Smallog.debug('CSettings value not changed: ' + key + ' = ' + castedValue); + } else + Smallog.debug( + "CSettings value not changed: " + key + " = " + castedValue + ); } else { - Smallog.error('CSettings Error: invalid type on: \'' + key + - '\'. Is: \'' + typeof this[key] + '\', applied: \'' + typeof castedValue + '\''); + Smallog.error( + "CSettings Error: invalid type on: '" + + key + + "'. Is: '" + + typeof this[key] + + "', applied: '" + + typeof castedValue + + "'" + ); } } return false; diff --git a/src/FPSta.ts b/src/FPSta.ts index 911ac75..4219c45 100644 --- a/src/FPSta.ts +++ b/src/FPSta.ts @@ -1,31 +1,30 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ -import {CComponent, CSettings, waitReady, WEAS} from '.'; +import { CComponent, CSettings, waitReady, WEAS } from "."; - -const ELM_ID = 'fpstats'; +const ELM_ID = "fpstats"; /** -* Custom Stats settings -* @public -* @extends {CSettings} -*/ + * Custom Stats settings + * @public + * @extends {CSettings} + */ export class FPSettings extends CSettings { - debugging: boolean = false; + debugging = false; } /** -* Custom FPS Stats module -* @public -* @extends {CComponent} -*/ + * Custom FPS Stats module + * @public + * @extends {CComponent} + */ export class FPStats extends CComponent { public settings: FPSettings = new FPSettings(); @@ -33,31 +32,30 @@ export class FPStats extends CComponent { // FPS private fpsHolder: HTMLElement; private lastUpdate: number = performance.now(); - private frameCount: number = 0; + private frameCount = 0; // usage private useHolder: HTMLElement; // cpu private cpuHolder: HTMLElement; private cpuBegin: number = performance.now(); private cpuEnd: number = performance.now(); - private cpuMS: number = 0; + private cpuMS = 0; // gpu private gpuHolder: HTMLElement; private gpuBegin: number = performance.now(); private gpuEnd: number = performance.now(); - private gpuMS: number = 0; + private gpuMS = 0; // audio private auProvider: WEAS = null; private audHolder: HTMLElement; - private audioMS: number = 0; + private audioMS = 0; private bpmHolder: HTMLDivElement; - /** - * Create hidden element - * @param {WEAS} audio (optional) - * @param {WEICUE} cue (optional) - */ + * Create hidden element + * @param {WEAS} audio (optional) + * @param {WEICUE} cue (optional) + */ constructor(audio?: WEAS) { super(); this.auProvider = audio; @@ -69,11 +67,11 @@ export class FPStats extends CComponent { } /** - * Make style - * @ignore - */ + * Make style + * @ignore + */ private injectCSS() { - const st = document.createElement('style'); + const st = document.createElement("style"); st.innerHTML = ` #${ELM_ID} { opacity: 0; @@ -94,58 +92,63 @@ export class FPStats extends CComponent { } /** - * Make dom - * @ignore - */ + * Make dom + * @ignore + */ private injectHTML() { // root - this.container = document.createElement('div'); + this.container = document.createElement("div"); this.container.id = ELM_ID; document.body.append(this.container); // fps - this.fpsHolder = document.createElement('div'); - this.fpsHolder.innerText = 'FPS: 0'; + this.fpsHolder = document.createElement("div"); + this.fpsHolder.innerText = "FPS: 0"; // cpu - this.cpuHolder = document.createElement('div'); - this.cpuHolder.innerText = 'CPU: 0.00 ms'; + this.cpuHolder = document.createElement("div"); + this.cpuHolder.innerText = "CPU: 0.00 ms"; // gpu - this.gpuHolder = document.createElement('div'); - this.gpuHolder.innerText = 'GPU: 0.00 ms'; + this.gpuHolder = document.createElement("div"); + this.gpuHolder.innerText = "GPU: 0.00 ms"; // usage - this.useHolder = document.createElement('div'); - this.useHolder.innerText = 'All: 0.00%'; + this.useHolder = document.createElement("div"); + this.useHolder.innerText = "All: 0.00%"; // append - this.container.append(this.fpsHolder, this.cpuHolder, this.gpuHolder, this.useHolder); + this.container.append( + this.fpsHolder, + this.cpuHolder, + this.gpuHolder, + this.useHolder + ); // audio if (this.auProvider) { - this.bpmHolder = document.createElement('div'); - this.bpmHolder.innerText = 'BPM: 0 ~ '; + this.bpmHolder = document.createElement("div"); + this.bpmHolder.innerText = "BPM: 0 ~ "; - this.audHolder = document.createElement('div'); - this.audHolder.innerText = 'Audio: 0 ms'; + this.audHolder = document.createElement("div"); + this.audHolder.innerText = "Audio: 0 ms"; this.container.append(this.bpmHolder, this.audHolder); } } /** - * update visible - * @public - * @return {Promise} - */ + * update visible + * @public + * @return {Promise} + */ public updateSettings(): Promise { // show or hide debug info return waitReady().then(() => { - if (this.settings.debugging) this.container.classList.add('show'); - else this.container.classList.remove('show'); + if (this.settings.debugging) this.container.classList.add("show"); + else this.container.classList.remove("show"); }); } /** - * Start measuring interval - * @public - * @param {boolean} cpu True if Cpu, false if GPU - */ + * Start measuring interval + * @public + * @param {boolean} cpu True if Cpu, false if GPU + */ public begin(cpu: boolean) { if (!this.settings.debugging) return; @@ -154,10 +157,10 @@ export class FPStats extends CComponent { } /** - * End measuring interval - * @public - * @param {boolean} cpu True if Cpu, false if GPU - */ + * End measuring interval + * @public + * @param {boolean} cpu True if Cpu, false if GPU + */ public end(cpu: boolean) { if (!this.settings.debugging) return; @@ -166,13 +169,13 @@ export class FPStats extends CComponent { } /** - * update the html representation - * @public - */ + * update the html representation + * @public + */ public update() { this.frameCount++; - this.cpuMS += (this.cpuEnd - this.cpuBegin); - this.gpuMS += (this.gpuEnd - this.gpuBegin); + this.cpuMS += this.cpuEnd - this.cpuBegin; + this.gpuMS += this.gpuEnd - this.gpuBegin; if (this.auProvider && this.auProvider.lastAudio) { this.audioMS = (this.audioMS + this.auProvider.lastAudio.ellapsed) / 2; @@ -180,7 +183,7 @@ export class FPStats extends CComponent { // only update text ~every second const now = performance.now(); - if (now < (this.lastUpdate + 1000)) return; + if (now < this.lastUpdate + 1000) return; // calculate const elapsd = (now - this.lastUpdate) / 1000; @@ -195,11 +198,16 @@ export class FPStats extends CComponent { this.gpuHolder.innerText = `GPU: ${gepeyu.toFixed(2)} ms`; this.useHolder.innerText = `All: ${yusage.toFixed(2)} %`; - if (this.audHolder) this.audHolder.innerText = `Audio: ${this.audioMS.toFixed(2)} ms`; - if (this.bpmHolder && this.auProvider.lastAudio && this.auProvider.lastAudio.bpm instanceof Array) { + if (this.audHolder) + this.audHolder.innerText = `Audio: ${this.audioMS.toFixed(2)} ms`; + if ( + this.bpmHolder && + this.auProvider.lastAudio && + this.auProvider.lastAudio.bpm instanceof Array + ) { let bts = 0; const bp = this.auProvider.lastAudio.bpm; - bp.forEach((b) => bts += b.value); + bp.forEach((b) => (bts += b.value)); bts /= bp.length; this.bpmHolder.innerText = `BPM: ${bts.toFixed(2)} ~`; } @@ -209,9 +217,9 @@ export class FPStats extends CComponent { } /** - * All back to 0 - * @public - */ + * All back to 0 + * @public + */ public reset() { this.frameCount = this.cpuMS = this.gpuMS = this.audioMS = 0; } diff --git a/src/ICUE.d.ts b/src/ICUE.d.ts index d01678b..080fc8b 100644 --- a/src/ICUE.d.ts +++ b/src/ICUE.d.ts @@ -1,234 +1,286 @@ /** -* @author 'Andrew' / https://github.com/profezzional -* -* Types for Wallpaper Engine integation for CUE SDK 3.0.+ -* -* @see https://wallpaper-engine.fandom.com/wiki/Web_Wallpaper_iCUE_Reference for reference -* @see http://forum.corsair.com/v3/showthread.php?t=179027 for the latest SDK version -* @see https://github.com/profezzional/iCUE-wallpaper-engine for orginal source -*/ + * @author 'Andrew' / https://github.com/profezzional + * + * Types for Wallpaper Engine integation for CUE SDK 3.0.+ + * + * @see https://wallpaper-engine.fandom.com/wiki/Web_Wallpaper_iCUE_Reference for reference + * @see http://forum.corsair.com/v3/showthread.php?t=179027 for the latest SDK version + * @see https://github.com/profezzional/iCUE-wallpaper-engine for orginal source + */ /** -* Contains information about SDK and CUE versions. -*/ + * Contains information about SDK and CUE versions. + */ type ProtocolDetails = { /** - * Boolean value that specifies if there were breaking changes between version of protocol implemented by server and client. - */ - breakingChanges: boolean, + * Boolean value that specifies if there were breaking changes between version of protocol implemented by server and client. + */ + breakingChanges: boolean; /** - * Integer number that specifies version of protocol that is implemented by current SDK. - * Numbering starts from 1. Always contains valid value even if there was no CUE found. - */ - sdkProtocolVersion: number, + * Integer number that specifies version of protocol that is implemented by current SDK. + * Numbering starts from 1. Always contains valid value even if there was no CUE found. + */ + sdkProtocolVersion: number; /** - * String containing version of SDK (like "1.0.0.1"). Always contains valid value even if there was no CUE found. - */ - sdkVersion: string, + * String containing version of SDK (like "1.0.0.1"). Always contains valid value even if there was no CUE found. + */ + sdkVersion: string; /** - * Integer number that specifies version of protocol that is implemented by CUE. - * Numbering starts from 1. If CUE was not found then this value will be 0. - */ - serverProtocolVersion: number, + * Integer number that specifies version of protocol that is implemented by CUE. + * Numbering starts from 1. If CUE was not found then this value will be 0. + */ + serverProtocolVersion: number; /** - * String containing version of CUE (like "1.0.0.1") or NULL if CUE was not found. - */ - serverVersion: number + * String containing version of CUE (like "1.0.0.1") or NULL if CUE was not found. + */ + serverVersion: number; }; /** -* Contains list of available device types -*/ -type DeviceType = 'CDT_Keyboard' | 'CDT_Mouse' | 'CDT_Headset' | 'CDT_Mousemat' | 'CDT_HeadsetStand' | 'CDT_CommanderPro' | 'CDT_LightingNodePro'; + * Contains list of available device types + */ +type DeviceType = + | "CDT_Keyboard" + | "CDT_Mouse" + | "CDT_Headset" + | "CDT_Mousemat" + | "CDT_HeadsetStand" + | "CDT_CommanderPro" + | "CDT_LightingNodePro"; /** -* Valid values for keyboard physical layouts. -*/ -type KeyboardPhysicalLayout = 'CPL_US' | 'CPL_UK' | 'CPL_JP' | 'CPL_KR' | 'CPL_BR'; + * Valid values for keyboard physical layouts. + */ +type KeyboardPhysicalLayout = + | "CPL_US" + | "CPL_UK" + | "CPL_JP" + | "CPL_KR" + | "CPL_BR"; /** -* Valid values for mouse physical layouts, number represents configurable mouse LEDs. -*/ -type MousePhysicalLayout = 'CPL_Zones1' | 'CPL_Zones2' | 'CPL_Zones3' | 'CPL_Zones4'; + * Valid values for mouse physical layouts, number represents configurable mouse LEDs. + */ +type MousePhysicalLayout = + | "CPL_Zones1" + | "CPL_Zones2" + | "CPL_Zones3" + | "CPL_Zones4"; /** -* Contains list of available physical layouts for keyboards and mice. -*/ -type PhysicalLayout = KeyboardPhysicalLayout | MousePhysicalLayout | 'CPL_Invalid'; + * Contains list of available physical layouts for keyboards and mice. + */ +type PhysicalLayout = + | KeyboardPhysicalLayout + | MousePhysicalLayout + | "CPL_Invalid"; /** -* Contains list of available logical layouts for keyboards. -*/ -type KeyboardLogicalLayout = 'CLL_US_Int' | 'CLL_NA' | 'CLL_EU' | 'CLL_UK' | 'CLL_BE' | 'CLL_BR' | 'CLL_CH' | 'CLL_CN' | 'CLL_DE' - | 'CLL_ES' | 'CLL_FR' | 'CLL_IT' | 'CLL_ND' | 'CLL_RU4' | 'CLL_JP' | 'CLL_KR' | 'CLL_TW' | 'CLL_MEX'; + * Contains list of available logical layouts for keyboards. + */ +type KeyboardLogicalLayout = + | "CLL_US_Int" + | "CLL_NA" + | "CLL_EU" + | "CLL_UK" + | "CLL_BE" + | "CLL_BR" + | "CLL_CH" + | "CLL_CN" + | "CLL_DE" + | "CLL_ES" + | "CLL_FR" + | "CLL_IT" + | "CLL_ND" + | "CLL_RU4" + | "CLL_JP" + | "CLL_KR" + | "CLL_TW" + | "CLL_MEX"; /** -* Contains list of available logical layouts for keyboards. -*/ -type LogicalLayout = KeyboardLogicalLayout | 'CLL_Invalid'; + * Contains list of available logical layouts for keyboards. + */ +type LogicalLayout = KeyboardLogicalLayout | "CLL_Invalid"; /** -* Contains information about device. -*/ + * Contains information about device. + */ type DeviceInfo = { /** - * ICUE Device ID (not set by default) - */ - id: number, + * ICUE Device ID (not set by default) + */ + id: number; /** - * Enum describing device type. - */ - type: DeviceType, + * Enum describing device type. + */ + type: DeviceType; /** - * Device model (like "K95RGB"). - */ - model: string, + * Device model (like "K95RGB"). + */ + model: string; /** - * Enum describing physical layout of the keyboard or mouse. - * If device is neither keyboard nor mouse then value is "CPL_Invalid". - */ + * Enum describing physical layout of the keyboard or mouse. + * If device is neither keyboard nor mouse then value is "CPL_Invalid". + */ physicalLayout: PhysicalLayout; /** - * Enum describing logical layout of the keyboard as set in CUE settings. - * If device is not keyboard then value is "CLL_Invalid". - */ + * Enum describing logical layout of the keyboard as set in CUE settings. + * If device is not keyboard then value is "CLL_Invalid". + */ logicalLayout: LogicalLayout; /** - * Number of controllable LEDs on the device. - */ + * Number of controllable LEDs on the device. + */ ledCount: number; /** - * Led positions on the device (not set by default) - */ + * Led positions on the device (not set by default) + */ leds: LedPosition[]; /** - * Contains list of device capabilities. - * First version of SDK only supports lighting, but future versions may also support other capabilities. - */ + * Contains list of device capabilities. + * First version of SDK only supports lighting, but future versions may also support other capabilities. + */ capsMask: { CDC_Lighting: boolean } | { CDC_None: boolean }; }; /** -* Contains led id and position of led rectangle. Most of the keys are rectangular. -* In case if key is not rectangular (like Enter in ISO/UK layout) it returns the smallest rectangle that fully contains the key. -*/ + * Contains led id and position of led rectangle. Most of the keys are rectangular. + * In case if key is not rectangular (like Enter in ISO/UK layout) it returns the smallest rectangle that fully contains the key. + */ type LedPosition = { /** - * For keyboards, mousemats and headset stands, height in mm; - * for DIY-devices, height in logical units. - */ - height: number, + * For keyboards, mousemats and headset stands, height in mm; + * for DIY-devices, height in logical units. + */ + height: number; /** - * For keyboards, mousemats and headset stands, width in mm; - * for DIY-devices, width in logical units. - */ - width: number, + * For keyboards, mousemats and headset stands, width in mm; + * for DIY-devices, width in logical units. + */ + width: number; /** - * For keyboards, mousemats and headset stands, top offset in mm; - * for DIY-devices, top offset in logical units. - */ - top: number, + * For keyboards, mousemats and headset stands, top offset in mm; + * for DIY-devices, top offset in logical units. + */ + top: number; /** - * For keyboards, mousemats and headset stands, left offset in mm; - * for DIY-devices, left offset in logical units. - */ - left: number, + * For keyboards, mousemats and headset stands, left offset in mm; + * for DIY-devices, left offset in logical units. + */ + left: number; /** - * Identifier of led. - */ - ledId: number, + * Identifier of led. + */ + ledId: number; /** - * Identifier of led. - */ - ledIdName: string + * Identifier of led. + */ + ledIdName: string; }; /** Contains information about led and its color. */ type LedColor = { /** - * Identifier of LED to set. - */ - ledId: number, + * Identifier of LED to set. + */ + ledId: number; /** - * Red brightness [0..255]. - */ - r: number, + * Red brightness [0..255]. + */ + r: number; /** - * Green brightness [0..255]. - */ - g: number, + * Green brightness [0..255]. + */ + g: number; /** - * Blue brightness [0..255]. - */ - b: number + * Blue brightness [0..255]. + */ + b: number; }; /** Main Interface */ -export interface ICUE { +export type ICUE = { /** - * Returns current status and version of iCUE SDK. - * @param callback A callback into which the protocol details are passed. - */ - getProtocolDetails(callback: (protocolDetails: ProtocolDetails) => void): void; + * Returns current status and version of iCUE SDK. + * @param callback A callback into which the protocol details are passed. + */ + getProtocolDetails( + callback: (protocolDetails: ProtocolDetails) => void + ): void; /** - * Returns the number of recognized iCUE compatible devices on the system. - * @param callback A callback into which the device count is passed. - */ + * Returns the number of recognized iCUE compatible devices on the system. + * @param callback A callback into which the device count is passed. + */ getDeviceCount(callback: (count: number) => void): void; /** - * Returns all information specific to a single device. - * @param deviceIndex The index of the device about which to get info. - * @param callback A callback into which the device info is passed. - */ - getDeviceInfo(deviceIndex: number, callback: (deviceInfo: DeviceInfo) => void): void; + * Returns all information specific to a single device. + * @param deviceIndex The index of the device about which to get info. + * @param callback A callback into which the device info is passed. + */ + getDeviceInfo( + deviceIndex: number, + callback: (deviceInfo: DeviceInfo) => void + ): void; /** - * Provides list of keyboard, mousemat, headset stand and DIY-devices LEDs with their physical (keyboard, mousemat and headset stand) or logical (DIY-devices) positions. - * @param deviceIndex The index of the device whose LED position to get. - * @param callback A callback into which the LED positions are passed. - */ - getLedPositionsByDeviceIndex(deviceIndex: number, callback: (leds: LedPosition[]) => void): void; + * Provides list of keyboard, mousemat, headset stand and DIY-devices LEDs with their physical (keyboard, mousemat and headset stand) or logical (DIY-devices) positions. + * @param deviceIndex The index of the device whose LED position to get. + * @param callback A callback into which the LED positions are passed. + */ + getLedPositionsByDeviceIndex( + deviceIndex: number, + callback: (leds: LedPosition[]) => void + ): void; /** - * Set specified leds to some colors. - * The color is retained until changed by successive calls. - * This function does not take logical layout into account, and returns control to the caller immediately. - * @param leds Array containing colors for each LED. - */ + * Set specified leds to some colors. + * The color is retained until changed by successive calls. + * This function does not take logical layout into account, and returns control to the caller immediately. + * @param leds Array containing colors for each LED. + */ setLedsColorsAsync(leds: LedColor[]): void; /** - * Updates all LEDs for given devices to one specific color. - * @param deviceIndexOrArray Index or indices of the device(s) whose LEDs to set to the specified color. - * @param ledColor The color to which to change the LEDs of the specified device(s). - */ - setAllLedsColorsAsync(deviceIndexOrArray: number | number[], ledColor: LedColor): void; + * Updates all LEDs for given devices to one specific color. + * @param deviceIndexOrArray Index or indices of the device(s) whose LEDs to set to the specified color. + * @param ledColor The color to which to change the LEDs of the specified device(s). + */ + setAllLedsColorsAsync( + deviceIndexOrArray: number | number[], + ledColor: LedColor + ): void; /** - * Updates all LEDs for given devices to one specific color. - * @param deviceIndexOrArray Index or indices of the device(s) whose LEDs to set to the specified color. - * @param ledColor The color to which to change the LEDs of the specified device(s). - */ - setLedColorsByImageData(deviceIndexOrArray: number | number[], encodedImageData, width: number, height: number): void; -} + * Updates all LEDs for given devices to one specific color. + * @param deviceIndexOrArray Index or indices of the device(s) whose LEDs to set to the specified color. + * @param ledColor The color to which to change the LEDs of the specified device(s). + */ + setLedColorsByImageData( + deviceIndexOrArray: number | number[], + encodedImageData, + width: number, + height: number + ): void; +}; diff --git a/src/LoadHelper.ts b/src/LoadHelper.ts index dee93ea..06220c4 100644 --- a/src/LoadHelper.ts +++ b/src/LoadHelper.ts @@ -1,23 +1,23 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -* -* @description -* Displays a html load bar with given progress. -* -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Displays a html load bar with given progress. + * + */ -import {waitReady} from '.'; +import { waitReady } from "."; -const mainColor = '#69dc00aa'; +const mainColor = "#69dc00aa"; /** -* Visual Reload-Bar -*/ + * Visual Reload-Bar + */ export class LoadHelper { private _outer: HTMLDivElement; private _bar: HTMLDivElement; @@ -26,8 +26,8 @@ export class LoadHelper { public progress = 0; /** - * Create and prepare when document is ready - */ + * Create and prepare when document is ready + */ constructor() { waitReady().then(() => { this.injectCSS(); @@ -38,11 +38,12 @@ export class LoadHelper { } /** - * Make custom style - * @ignore - */ + * Make custom style + * @ignore + * @returns {void} + */ private injectCSS() { - const st = document.createElement('style'); + const st = document.createElement("style"); st.innerHTML = ` #ldhlpr-bar { position: absolute; @@ -75,23 +76,24 @@ export class LoadHelper { } /** - * Make custom html elements - * @ignore - */ + * Make custom html elements + * @ignore + * @returns {void} + */ private injectHTML() { - this._outer = document.createElement('div'); - this._outer.id = 'loadhelper'; - this._bar = document.createElement('div'); - this._bar.id = 'ldhlpr-bar'; - this._tex = document.createElement('h1'); - this._tex.id = 'ldhlpr-text'; + this._outer = document.createElement("div"); + this._outer.id = "loadhelper"; + this._bar = document.createElement("div"); + this._bar.id = "ldhlpr-bar"; + this._tex = document.createElement("h1"); + this._tex.id = "ldhlpr-text"; this._outer.append(this._bar, this._tex); document.body.append(this._outer); } - public setText(text = '') { - let txt = '--- Loading ---'; - if (text != '') txt += `
      ${text}`; + public setText(text = "") { + let txt = "--- Loading ---"; + if (text != "") txt += `
      ${text}`; this._tex.innerHTML = txt; } @@ -101,20 +103,21 @@ export class LoadHelper { } /** - * always reset bar to 0 - * @public - * @param {boolean} visible - */ + * always reset bar to 0 + * @public + * @param {boolean} visible show / hide + * @returns {void} + */ public show(visible: boolean) { const e1 = this._bar; const e2 = this._tex; - e1.classList.remove('show'); - e2.classList.remove('show'); + e1.classList.remove("show"); + e2.classList.remove("show"); if (visible) { setTimeout(() => { - e1.classList.add('show'); - e2.classList.add('show'); + e1.classList.add("show"); + e2.classList.add("show"); }, 10); } } -}; +} diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index 35e9748..4eb63dd 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -1,57 +1,57 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -* -* @description -* Displays a html reload bar for a given Time. -* -*/ - -import {CComponent, CSettings, waitReady} from './'; + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + * Displays a html reload bar for a given Time. + * + */ +import { CComponent, CSettings, waitReady } from "./"; /** -* Reload-bar settings -*/ + * Reload-bar settings + */ class ReloadSettings extends CSettings { - reload_seconds: number = 3; + reload_seconds = 3; } /** -* Visual Reload-Bar -*/ + * Visual Reload-Bar + */ export class ReloadHelper extends CComponent { private _outer: HTMLDivElement; private _bar: HTMLDivElement; private _tex: HTMLDivElement; /** - * @public - */ + * @public + */ public settings: ReloadSettings = new ReloadSettings(); /** - * Create and prepare when document is ready - */ + * Create and prepare when document is ready + */ constructor() { super(); waitReady().then(() => { this.injectCSS(); this.injectHTML(); - this.setText('Reloading...'); + this.setText("Reloading..."); }); } /** - * Make custom style - * @ignore - */ + * Make custom style + * @ignore + * @returns {void} + */ private injectCSS() { - const st = document.createElement('style'); + const st = document.createElement("style"); st.innerHTML = ` #reload-bar { position: absolute; @@ -97,16 +97,17 @@ export class ReloadHelper extends CComponent { } /** - * Make custom html elements - * @ignore - */ + * Make custom html elements + * @ignore + * @returns {void} + */ private injectHTML() { - this._outer = document.createElement('div'); - this._outer.id = 'reloadhelper'; - this._bar = document.createElement('div'); - this._bar.id = 'reload-bar'; - this._tex = document.createElement('h1'); - this._tex.id = 'reload-text'; + this._outer = document.createElement("div"); + this._outer.id = "reloadhelper"; + this._bar = document.createElement("div"); + this._bar.id = "reload-bar"; + this._tex = document.createElement("h1"); + this._tex.id = "reload-text"; this._outer.append(this._bar, this._tex); document.body.append(this._outer); } @@ -116,29 +117,30 @@ export class ReloadHelper extends CComponent { } /** - * always reset bar to 0 - * @public - * @param {boolean} visible - */ + * always reset bar to 0 + * @public + * @param {boolean} visible show / hide + * @returns {void} + */ public show(visible: boolean) { const e1 = this._bar; const e2 = this._tex; - e1.classList.remove('show'); - e2.classList.remove('show'); + e1.classList.remove("show"); + e2.classList.remove("show"); if (visible) { setTimeout(() => { - e1.classList.add('show'); - e2.classList.add('show'); + e1.classList.add("show"); + e2.classList.add("show"); }, 100); } } /** - * dont print IMPL error - * @public - * @return {Promise} - */ + * dont print IMPL error + * @public + * @return {Promise} finished + */ public updateSettings(): Promise { return Promise.resolve(); } -}; +} diff --git a/src/Smallog.ts b/src/Smallog.ts index b18229a..57a8c29 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -1,124 +1,123 @@ - /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ /* eslint-disable no-unused-vars */ /** -* @see {Smalog} -* @public -*/ + * @see {Smalog} + * @public + */ export enum LogLevel { /** - * Print only error messages - */ + * Print only error messages + */ Error = 0, /** - * Print error and info messages - */ + * Print error and info messages + */ Info = 1, /** - * Print all messages - */ - Debug = 2 + * Print all messages + */ + Debug = 2, } /** -* Small logging util, with name/time prefix & log levels -*/ + * Small logging util, with name/time prefix & log levels + */ class Smalog { logLevel: LogLevel = LogLevel.Debug; // @todo change default logging level - preFix = '[Smallog] '; + preFix = "[Smallog] "; printTime = false; /** - * trace exception calls - * @param {string} def error message - * @param {number} depth which call to pick - * @return {string} error message - * @ignore - */ + * trace exception calls + * @param {string} def error message + * @param {number} depth which call to pick + * @return {string} error message + * @ignore + */ traceCall(def: string, depth = 3): string { try { - throw new Error('trace()'); + throw new Error("trace()"); } catch (e) { // Examine e.stack here if (e.stack) { const splt = e.stack.split(/\n/); let trim = splt[depth].trim(); - if (trim.indexOf('at ') == 0) trim = trim.substring(3); - if (splt.length > depth) return '[' + trim + '] '; + if (trim.indexOf("at ") == 0) trim = trim.substring(3); + if (splt.length > depth) return "[" + trim + "] "; } } return def; } /** - * get logging output level - * @return {LogLevel} current - */ + * get logging output level + * @return {LogLevel} current + */ getLevel() { return this.logLevel; } /** - * set logging prefix - * @param {string} pre new prefix - * @return {void} nothing - */ + * set logging prefix + * @param {string} pre new prefix + * @return {void} nothing + */ setPrefix(pre: string) { this.preFix = pre; } /** - * set time prefix - * @param {boolean} print true to print time - * @return {void} nothing - */ + * set time prefix + * @param {boolean} print true to print time + * @return {void} nothing + */ setPrintTime(print: boolean) { this.printTime = print; } /** - * print error message - * @param {string} msg log - * @param {string} hdr overwrite header - * @return {void} nothing - */ + * print error message + * @param {string} msg log + * @param {string} hdr overwrite header + * @return {void} nothing + */ error(msg: string, hdr: string = this.preFix) { - if (this.printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + if (this.printTime) msg = "[" + new Date().toLocaleString() + "] " + msg; console.error(hdr + msg); } /** - * print info message - * @param {string} msg log - * @param {string} hdr overwrite header - * @return {void} nothing - */ + * print info message + * @param {string} msg log + * @param {string} hdr overwrite header + * @return {void} nothing + */ info(msg: string, hdr: string = this.preFix) { if (this.logLevel >= 1) { - if (this.printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + if (this.printTime) msg = "[" + new Date().toLocaleString() + "] " + msg; if (this.logLevel >= 2) hdr = this.traceCall(hdr); console.info(hdr + msg); } } /** - * print debug message - * @param {string} msg log - * @param {string} hdr overwrite header - * @return {void} nothing - */ + * print debug message + * @param {string} msg log + * @param {string} hdr overwrite header + * @return {void} nothing + */ debug(msg: string, hdr: string = this.preFix) { if (this.logLevel >= 2) { - if (this.printTime) msg = ('[' + new Date().toLocaleString() + '] ') + msg; + if (this.printTime) msg = "[" + new Date().toLocaleString() + "] " + msg; console.debug(hdr + msg); } } diff --git a/src/Util.ts b/src/Util.ts index 2ed4739..751a7bd 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -1,13 +1,15 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -* -* @ignore -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @ignore + */ + +import { WascInterface, wascWorker } from "."; // promise resolve queue const promQueue: ((val) => void)[] = []; @@ -17,17 +19,20 @@ const workQueue = () => { call(true); } }; -document.addEventListener('DOMContentLoaded', workQueue, false); +document.addEventListener("DOMContentLoaded", workQueue, false); /** -* Shorthand Document ready wrapper,\n resolves promise when html document is ready -* @public -* @return {Promise} -*/ + * Shorthand Document ready wrapper + * @public + * @return {Promise} resolves when html document is ready + */ export function waitReady() { return new Promise((resolve) => { // If document is already loaded, run method - if (document.readyState === 'interactive' || document.readyState === 'complete') { + if ( + document.readyState === "interactive" || + document.readyState === "complete" + ) { resolve(true); } // Otherwise, wait until document is loaded @@ -36,20 +41,23 @@ export function waitReady() { } /** -* @todo check for rgba errors -* Convert helper -* @param {string} r_g_b format: "r g b" where each is float 0-1 -* @param {number} mlt multiplier (default 255) -* @return {Object} {r,g,b,a} with float 0-mlt -*/ -export function rgbToObj(r_g_b:string, mlt = 255): {r: number, g:number, b:number, a:number} { + * @todo check for rgba errors + * Convert helper + * @param {string} r_g_b format: "r g b" where each is float 0-1 + * @param {number} mlt multiplier (default 255) + * @return {Object} {r,g,b,a} with float 0-mlt + */ +export function rgbToObj( + r_g_b: string, + mlt = 255 +): { r: number; g: number; b: number; a: number } { // fix support for rgba strings - const brackI = r_g_b.indexOf('('); + const brackI = r_g_b.indexOf("("); if (brackI > -1) { - r_g_b = r_g_b.substring(brackI + 1, r_g_b.indexOf(')')); + r_g_b = r_g_b.substring(brackI + 1, r_g_b.indexOf(")")); } // do splitting and multiplying - const spl = r_g_b.split(' ') as any[]; + const spl = r_g_b.split(" ") as any[]; for (let i = 0; i < spl.length; i++) { spl[i] = isNaN(spl[i]) ? 0 : Math.min(mlt, Math.max(0, spl[i] * mlt)); } @@ -57,37 +65,68 @@ export function rgbToObj(r_g_b:string, mlt = 255): {r: number, g:number, b:numbe r: spl[0] || 0, g: spl[1] || 0, b: spl[2] || 0, - a: spl[3] || (1 * mlt), + a: spl[3] || 1 * mlt, }; } /** -* Convert helper -* @param {string} r_g_b format: "r g b" where each is float 0-1 -* @return {Object} {h,s,l} with float 0-1 -*/ -export function rgbToHSL(r_g_b: string): {h: number, s:number, l:number} { + * Convert helper + * @param {string} r_g_b format: "r g b" where each is float 0-1 + * @return {Object} {h,s,l} with float 0-1 + */ +export function rgbToHSL(r_g_b: string): { h: number; s: number; l: number } { const cO = rgbToObj(r_g_b, 1); const ma = Math.max(cO.r, cO.g, cO.b); const mi = Math.min(cO.r, cO.g, cO.b); - const hsl = {h: 0, s: 0, l: (mi + ma) / 2}; + const hsl = { h: 0, s: 0, l: (mi + ma) / 2 }; const t = ma - mi; - switch (hsl.s = hsl.l <= .5 ? t / (ma + mi) : t / (2 - ma - mi), ma) { - case cO.r: - hsl.h = (cO.g - cO.b) / t + (cO.g < cO.b ? 6 : 0); - break; - case cO.g: - hsl.h = (cO.b - cO.r) / t + 2; - break; - case cO.b: - hsl.h = (cO.r - cO.g) / t + 4; + switch (((hsl.s = hsl.l <= 0.5 ? t / (ma + mi) : t / (2 - ma - mi)), ma)) { + case cO.r: + hsl.h = (cO.g - cO.b) / t + (cO.g < cO.b ? 6 : 0); + break; + case cO.g: + hsl.h = (cO.b - cO.r) / t + 2; + break; + case cO.b: + hsl.h = (cO.r - cO.g) / t + 4; } hsl.h /= 6; return hsl; } +/** + * Proxy shared or non-shared module call + * @param {string} bldr file to load + * @param {number} memory max mem (shared needs limit) + * @param {any} options module options + * @param {boolean} useWorker or load inline + * @returns {Promise} loaded module + */ +export function sharedWorker( + bldr: string, + memory = 4096, + options?: any, + useWorker = true +): Promise { + const iso = + window["crossOriginIsolated"] === true || + window.location.protocol == "file:"; + if (iso) bldr = bldr.replace(".wasm", ".shared.wasm"); + return wascWorker(bldr, memory, iso, options, useWorker); +} /** * Shorthand for Typed array stuff + * @public */ -export const AnyTypedArray: Uint32Array | Float32Array | Float64Array | Uint8Array | Int8Array | Uint16Array | Int16Array | Int32Array | BigUint64Array | BigInt64Array = null; +export const AnyTypedArray: + | Uint32Array + | Float32Array + | Float64Array + | Uint8Array + | Int8Array + | Uint16Array + | Int16Array + | Int32Array + | BigUint64Array + | BigInt64Array = null; diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 07ed1cd..0028ca3 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -1,53 +1,51 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ -import {CComponent, CSettings, rgbToObj, Smallog, waitReady, WEAS} from '.'; -import {ICUE} from './ICUE'; +import { CComponent, CSettings, rgbToObj, Smallog, waitReady, WEAS } from "."; +import { ICUE } from "./ICUE"; +const IMG_SRC = "./img/icue.png"; -const IMG_SRC = './img/icue.png'; - -const ClassName: string = '[WEICUE] '; -const canvasX: number = 23; -const canvasY: number = 7; -const WaitTime: number = 30; -const Transition: number = 3; - +const ClassName = "[WEICUE] "; +const canvasX = 23; +const canvasY = 7; +const WaitTime = 30; +const Transition = 3; /** -* iCUE processing settings -* @public -* @extends {CSettings} -*/ + * iCUE processing settings + * @public + * @extends {CSettings} + */ export class CUESettings extends CSettings { - public icue_mode: number = 1; - public icue_area_xoff: number = 50; - public icue_area_yoff: number = 90; - public icue_area_width: number = 75; - public icue_area_height: number = 30; - public icue_area_blur: number = 5; - public icue_area_decay: number = 15; - public icue_main_color: string = '0 0.8 0'; + public icue_mode = 1; + public icue_area_xoff = 50; + public icue_area_yoff = 90; + public icue_area_width = 75; + public icue_area_height = 30; + public icue_area_blur = 5; + public icue_area_decay = 15; + public icue_main_color = "0 0.8 0"; // AudiOrbits bg Color; used as "decay"-color aswell - public main_color: string = '0 0 0'; + public main_color = "0 0 0"; } /** -* WEICUE -*
      -* Wallpaper Engine iCUE effects for web wallpapers -*
      -* Uses several different methods to create -* Lighting effects for Corsair ICUE devices. -* @public -* @extends {CComponent} -*/ + * WEICUE + *
      + * Wallpaper Engine iCUE effects for web wallpapers + *
      + * Uses several different methods to create + * Lighting effects for Corsair ICUE devices. + * @public + * @extends {CComponent} + */ export class WEICUE extends CComponent { private cue: ICUE; private weas: WEAS; @@ -66,24 +64,24 @@ export class WEICUE extends CComponent { // runtime values public settings: CUESettings = new CUESettings(); - public isAvailable: boolean = false; - public PAUSED: boolean = false; + public isAvailable = false; + public PAUSED = false; /** - * Starts listening for led/icue plugin - * and prepares helper elements - * @param {WEAS} weas Audio supplier for non-projection mode - */ + * Starts listening for led/icue plugin + * and prepares helper elements + * @param {WEAS} weas Audio supplier for non-projection mode + */ constructor(weas: WEAS) { super(); this.weas = weas; // Plugin handler - window['wallpaperPluginListener'] = { + window["wallpaperPluginListener"] = { onPluginLoaded: (name: string, version: string) => { const lower = name.toLocaleLowerCase(); - if (lower === 'cue' || lower === 'led') { - this.cue = window['cue']; + if (lower === "cue" || lower === "led") { + this.cue = window["cue"]; this.isAvailable = true; Smallog.debug(`Plugin loaded: ${name}, v${version}`, ClassName); } @@ -99,11 +97,12 @@ export class WEICUE extends CComponent { } /** - * style for iCue messages, preview and helper - * @ignore - */ + * style for iCue messages, preview and helper + * @ignore + * @returns {void} + */ private injectCSS() { - const st = document.createElement('style'); + const st = document.createElement("style"); st.innerHTML = ` #icueholder { opacity: 0; @@ -150,26 +149,28 @@ export class WEICUE extends CComponent { } /** - * Prepare html elements - * @ignore - */ + * Prepare html elements + * @ignore + * @returns {void} + */ private injectHTML() { // create container - this.holder = document.createElement('div'); - this.holder.id = 'icueholder'; + this.holder = document.createElement("div"); + this.holder.id = "icueholder"; // create icon (no ref needed) - const imgg = document.createElement('img'); - imgg.id = 'icuelogo'; - imgg.setAttribute('src', IMG_SRC); - imgg.setAttribute('alt', 'ICUE Icon'); + const imgg = document.createElement("img"); + imgg.id = "icuelogo"; + imgg.setAttribute("src", IMG_SRC); + imgg.setAttribute("alt", "ICUE Icon"); // make text holder - this.texter = document.createElement('div'); - this.texter.id = 'icuetext'; + this.texter = document.createElement("div"); + this.texter.id = "icuetext"; // preview area - this.preview = document.createElement('div'); - this.preview.classList.add('cuePreview'); - this.preview.innerHTML = '

      This is the LED/iCUE Projection-Area preview.
      It will hide automatically.

      You can disable LED/iCUE projection under:
      "Settings > 💡 LED/iCUE > Projection-mode = None"

      '; + this.preview = document.createElement("div"); + this.preview.classList.add("cuePreview"); + this.preview.innerHTML = + '

      This is the LED/iCUE Projection-Area preview.
      It will hide automatically.

      You can disable LED/iCUE projection under:
      "Settings > 💡 LED/iCUE > Projection-mode = None"

      '; // append image and text this.holder.append(imgg, this.texter); @@ -177,58 +178,59 @@ export class WEICUE extends CComponent { } /** - * show a message by icue - * @param {string} msg - * @param {boolean} waiting - * @ignore - */ - private icueMessage(msg: string, waiting :boolean = false) { - Smallog.debug('MSG: ' + msg, ClassName); + * show a message by icue + * @param {string} msg show text + * @param {boolean} waiting use Wait or Transition time + * @ignore + * @returns {void} + */ + private icueMessage(msg: string, waiting = false) { + Smallog.debug("MSG: " + msg, ClassName); // set text this.texter.innerHTML = msg; // show - this.holder.classList.add('show'); - if (waiting) this.holder.classList.add('waiting'); + this.holder.classList.add("show"); + if (waiting) this.holder.classList.add("waiting"); // hide again - const waiTime = waiting ? (WaitTime) : (Transition * 1000 + 4000); + const waiTime = waiting ? WaitTime : Transition * 1000 + 4000; setTimeout(() => { - this.holder.classList.remove('show'); - if (waiting) this.holder.classList.remove('waiting'); + this.holder.classList.remove("show"); + if (waiting) this.holder.classList.remove("waiting"); }, waiTime); } /** - * helper - * @param {boolean} inPx suffix "px" string to number (allows direct css use) - * @return {Object} area - * @ignore - */ + * helper + * @param {boolean} inPx suffix "px" string to number (allows direct css use) + * @return {Object} area + * @ignore + */ private getArea(inPx = false) { const sett = this.settings; const wwid = window.innerWidth; const whei = window.innerHeight; - const w = wwid * sett.icue_area_width / 100; - const h = whei * sett.icue_area_height / 100; - const l = ((wwid - w) * sett.icue_area_xoff / 100); - const t = ((whei - h) * sett.icue_area_yoff / 100); + const w = (wwid * sett.icue_area_width) / 100; + const h = (whei * sett.icue_area_height) / 100; + const l = ((wwid - w) * sett.icue_area_xoff) / 100; + const t = ((whei - h) * sett.icue_area_yoff) / 100; return { - width: w + (inPx ? 'px' : ''), - height: h + (inPx ? 'px' : ''), - left: l + (inPx ? 'px' : ''), - top: t + (inPx ? 'px' : ''), + width: w + (inPx ? "px" : ""), + height: h + (inPx ? "px" : ""), + left: l + (inPx ? "px" : ""), + top: t + (inPx ? "px" : ""), }; } /** - * convert data for icue - * @param {ImageData} imageData - * @return {string} - * @ignore - */ + * convert data for icue + * @param {ImageData} imageData source + * @return {string} encoded image + * @ignore + */ private getEncodedCanvasImageData(imageData: ImageData) { const colorArray = []; for (let d = 0; d < imageData.data.length; d += 4) { - const write = d / 4 * 3; + const write = (d / 4) * 3; colorArray[write] = imageData.data[d]; colorArray[write + 1] = imageData.data[d + 1]; colorArray[write + 2] = imageData.data[d + 2]; @@ -237,19 +239,25 @@ export class WEICUE extends CComponent { } /** - * canvas blur helper function - * @param {HTMLCanvasElement} canvas - * @param {CanvasRenderingContext2D} ctx - * @param {number} blur - * @ignore - */ - private gBlurCanvas(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, blur: number) { + * canvas blur helper function + * @param {HTMLCanvasElement} canvas source & target + * @param {CanvasRenderingContext2D} ctx context + * @param {number} blur amount + * @ignore + * @returns {void} + */ + private gBlurCanvas( + canvas: HTMLCanvasElement, + ctx: CanvasRenderingContext2D, + blur: number + ) { let sum = 0; const delta = 5; const alpha_left = 1 / (2 * Math.PI * delta * delta); const step = blur < 3 ? 1 : 2; - let x; let weight; + let x; + let weight; for (let y = -blur; y <= blur; y += step) { for (x = -blur; x <= blur; x += step) { weight = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta)); @@ -258,7 +266,11 @@ export class WEICUE extends CComponent { } for (let y = -blur; y <= blur; y += step) { for (x = -blur; x <= blur; x += step) { - ctx.globalAlpha = alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta)) / sum * blur * blur; + ctx.globalAlpha = + ((alpha_left * Math.exp(-(x * x + y * y) / (2 * delta * delta))) / + sum) * + blur * + blur; ctx.drawImage(canvas, x, y); } } @@ -266,40 +278,40 @@ export class WEICUE extends CComponent { } /** - * Show waiting message and init canvas - * @ignore - */ + * Show waiting message and init canvas + * @ignore + * @returns {void} + */ private init() { const sett = this.settings; // dont initialize if disabled if (sett.icue_mode == 0) return; - this.icueMessage('LED: waiting for plugin.', true); + this.icueMessage("LED: waiting for plugin.", true); this.initCUE(0); - Smallog.debug('init...', ClassName); + Smallog.debug("init...", ClassName); // recreate if reinit if (this.icueInterval) clearInterval(this.icueInterval); if (this.helperCanvas) document.body.removeChild(this.helperCanvas); // setup canvas - this.helperCanvas = document.createElement('canvas'); - this.helperCanvas.id = 'helpCvs'; + this.helperCanvas = document.createElement("canvas"); + this.helperCanvas.id = "helpCvs"; this.helperCanvas.width = canvasX; this.helperCanvas.height = canvasY; - this.helperCanvas.style.display = 'none'; - this.helperContext = this.helperCanvas.getContext('2d'); + this.helperCanvas.style.display = "none"; + this.helperContext = this.helperCanvas.getContext("2d"); document.body.appendChild(this.helperCanvas); // update devices about every 33ms/30fps. iCue doesnt really support higher values this.icueInterval = window.setInterval(() => this.updateFrame(), 1000 / 30); } - /** - * show or hide preview - * @public - * @return {Promise} finished - */ + * show or hide preview + * @public + * @return {Promise} finished + */ public updateSettings(): Promise { // reset timeout? if (this.prevTimeout) { @@ -309,31 +321,32 @@ export class WEICUE extends CComponent { // update / show preview if (this.isAvailable && this.preview && this.settings.icue_mode == 1) { Object.assign(this.preview.style, this.getArea(true)); - this.preview.classList.add('show'); + this.preview.classList.add("show"); this.prevTimeout = setTimeout(() => { - this.preview.classList.remove('show'); + this.preview.classList.remove("show"); }, 6000); } return Promise.resolve(); } /** - * will initialize ICUE api & usage - * @param {number} count Retries (will stop at 100) - * @ignore - */ + * will initialize ICUE api & usage + * @param {number} count Retries (will stop at 100) + * @ignore + * @returns {void} + */ private initCUE(count: number) { // wait for plugins if (!this.isAvailable) { if (count < 100) setTimeout(() => this.initCUE(++count), 150); - else this.icueMessage('LED: Plugin not found!'); + else this.icueMessage("LED: Plugin not found!"); return; } // setup devices this.icueDevices = []; this.cue.getDeviceCount((deviceCount) => { - this.icueMessage('LED: Found ' + deviceCount + ' devices.'); + this.icueMessage("LED: Found " + deviceCount + " devices."); for (let xi = 0; xi < deviceCount; xi++) { const xl = xi; this.cue.getDeviceInfo(xl, (info) => { @@ -348,16 +361,25 @@ export class WEICUE extends CComponent { } /** - * do the thing... - * @ignore - */ + * do the thing... + * @ignore + * @returns {void} + */ private updateFrame() { const sett = this.settings; - if (this.PAUSED || !this.isAvailable || sett.icue_mode == 0 || this.icueDevices.length < 1) return; + if ( + this.PAUSED || + !this.isAvailable || + sett.icue_mode == 0 || + this.icueDevices.length < 1 + ) + return; // projection mode if (sett.icue_mode == 1) { // get scaled down image data and encode it for icue - const encDat = this.getEncodedCanvasImageData(this.helperContext.getImageData(0, 0, canvasX, canvasY)); + const encDat = this.getEncodedCanvasImageData( + this.helperContext.getImageData(0, 0, canvasX, canvasY) + ); // update all icueDevices with data for (let xi = 0; xi < this.icueDevices.length; xi++) { this.cue.setLedColorsByImageData(xi, encDat, canvasX, canvasY); @@ -369,7 +391,7 @@ export class WEICUE extends CComponent { let mlt = 255; if (this.weas.hasAudio()) { const aud = this.weas.lastAudio; - mlt *= aud.average / aud.range / aud.intensity * 10; + mlt *= (aud.average / aud.range / aud.intensity) * 10; } // get lol objects const ledCol = rgbToObj(sett.icue_main_color, mlt); @@ -381,13 +403,20 @@ export class WEICUE extends CComponent { } /** - * copy main canvas portion to our helper - * @public - * @param {HTMLCanvasElementq} mainCanvas - */ + * copy main canvas portion to our helper + * @public + * @param {HTMLCanvasElementq} mainCanvas target + * @returns {void} + */ public updateCanvas(mainCanvas: HTMLCanvasElement) { const sett = this.settings; - if (!this.isAvailable || !mainCanvas || sett.icue_mode == 0 || this.icueDevices.length < 1) return; + if ( + !this.isAvailable || + !mainCanvas || + sett.icue_mode == 0 || + this.icueDevices.length < 1 + ) + return; if (sett.icue_mode == 1) { // get helper vars @@ -395,12 +424,25 @@ export class WEICUE extends CComponent { const hctx = this.helperContext; // get real rgb values const cO = rgbToObj(sett.main_color); - hctx.fillStyle = `rgba(${cO.r}, ${cO.g}, ${cO.b}, ${sett.icue_area_decay / 100})`; + hctx.fillStyle = `rgba(${cO.r}, ${cO.g}, ${cO.b}, ${ + sett.icue_area_decay / 100 + })`; hctx.fillRect(0, 0, canvasX, canvasY); // scale down and copy the image to the helper canvas - hctx.drawImage(mainCanvas, area.left, area.top, area.width, area.height, 0, 0, canvasX, canvasY); + hctx.drawImage( + mainCanvas, + area.left, + area.top, + area.width, + area.height, + 0, + 0, + canvasX, + canvasY + ); // blur the helper projection canvas - if (sett.icue_area_blur > 0) this.gBlurCanvas(this.helperCanvas, hctx, sett.icue_area_blur); + if (sett.icue_area_blur > 0) + this.gBlurCanvas(this.helperCanvas, hctx, sett.icue_area_blur); } } } diff --git a/src/WEWA.ts b/src/WEWA.ts index 7b3927f..f2e2d9e 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -87,10 +87,10 @@ export class WEWWA { private isPaused = false; /** - * Check if we are running in Web-Mode - * if yes => iniitialize, else => do nothing - * @param {Function} finished Callback for initializing the wallpaper - */ + * Check if we are running in Web-Mode + * if yes => iniitialize, else => do nothing + * @param {Function} finished Callback for initializing the wallpaper + */ constructor(finished) { if (window[wral]) { Smallog.info("detected wallpaper engine => Standby.", LogHead); @@ -123,9 +123,10 @@ export class WEWWA { } /** - * Initialize the Web Adapter - * @ignore - */ + * Initialize the Web Adapter + * @ignore + * @returns {void} + */ private init() { WascUtil.myFetch(proj, "json").then((proj) => { if (proj.type != "web") { @@ -148,9 +149,10 @@ export class WEWWA { } /** - * Load last settings from localStorage - * @ignore - */ + * Load last settings from localStorage + * @ignore + * @returns {void} + */ private loadStorage() { const props = this.project.general.properties; const last = localStorage.getItem("wewwaLastProps"); @@ -166,9 +168,10 @@ export class WEWWA { } /** - * CSS Insertion - * @ignore - */ + * CSS Insertion + * @ignore + * @returns {void} + */ private addStyle() { const st = document.createElement("style"); // precalculation @@ -274,8 +277,8 @@ export class WEWWA { #wewwaMenu.open, #wewwaIcon.open { transform: translateX(min(-${percentageWidth * 1.1}vw, -${Math.floor( - minWidthPx * 1.1 -)}px)); + minWidthPx * 1.1 + )}px)); transition: transform 500ms ease; } @@ -298,12 +301,12 @@ export class WEWWA { } /** - * HTML Creation - * @param {string} lang WE language - * @ignore - */ + * HTML Creation + * @param {string} lang WE language + * @ignore + * @returns {void} + */ private addMenu(lang) { - const self = this; if (this.htmlMenu) { document.body.removeChild(this.htmlMenu); document.body.removeChild(this.htmlIcon); @@ -324,7 +327,7 @@ export class WEWWA { // create preview img wrap this.addMenuHeader(ce, proj); // create table with settings - this.addMenuSettings(ce, proj, self, lang, props); + this.addMenuSettings(ce, proj, this, lang, props); // Add Footer this.addMenuFooter(ce); // finally add the menu to the DOM @@ -335,12 +338,12 @@ export class WEWWA { } /** - * Adds the Menu Icon - * @param {Function} ce CreateElement - * @param {Element} menu - * @ignore - */ - private addMenuIcon(ce: (e: any) => any, menu = this.htmlMenu) { + * Adds the Menu Icon + * @param {Function} ce CreateElement + * @ignore + * @returns {void} + */ + private addMenuIcon(ce: (e: any) => any) { const icon = (this.htmlIcon = ce("div")); icon.id = "wewwaIcon"; icon.addEventListener("click", () => { @@ -363,26 +366,25 @@ export class WEWWA { } /** - * Adds the actual Wallpaper Props as HTML - * @param {Function} ce Create Element wrapper - * @param {Object} proj project - * @param {object} self this - * @param {string} lang - * @param {object} props - * @param {Element} menu - * @ignore - */ + * Adds the actual Wallpaper Props as HTML + * @param {Function} ce Create Element wrapper + * @param {Object} proj project + * @param {object} self this + * @param {string} lang uage + * @param {object} props options + * @ignore + * @returns {void} + */ private addMenuSettings( ce: (e: any) => any, proj: any, self: this, lang: string, - props: any, - menu = this.htmlMenu + props: any ) { const tbl = ce("table"); tbl.innerHTML = - ' '; + ' '; const tblBody = ce("tbody"); tbl.append(tblBody); @@ -405,7 +407,7 @@ export class WEWWA { const pauseBox = ce("input"); pauseBox.setAttribute("type", "checkbox"); pauseBox.setAttribute("checked", this.pauseOnUnfocus); - pauseBox.addEventListener("change", function (e) { + pauseBox.addEventListener("change", function () { // eslint-disable-next-line no-invalid-this self.pauseOnUnfocus = this.checked; // unpause if paused @@ -450,14 +452,15 @@ export class WEWWA { // pre-footer for resetting saved settings // finish up menu - menu.append(tbl); + this.htmlMenu.append(tbl); } /** - * Add missing default localization strings - * @param {Object} local - * @ignore - */ + * Add missing default localization strings + * @param {Object} local languageObj + * @ignore + * @returns {void} + */ private mergeLocals(local: any) { const locDefs = { ui_browse_properties_scheme_color: "Scheme color", @@ -473,19 +476,19 @@ export class WEWWA { } /** - * Adds the Footer Link to the Menu - * @param {Function} ce create element - * @param {Element} menu - * @ignore - */ - private addMenuFooter(ce: (e: any) => any, menu = this.htmlMenu) { + * Adds the Footer Link to the Menu + * @param {Function} ce create element + * @ignore + * @returns {void} + */ + private addMenuFooter(ce: (e: any) => any) { const preFoot = ce("div"); preFoot.innerHTML = "
      "; const rst = ce("a"); rst.classList.add("red"); rst.innerHTML = "Reset ↩️"; - rst.addEventListener("click", (e) => { + rst.addEventListener("click", () => { if ( !window.confirm( "This action will clear ALL local data!\r\n\r\nAre you sure?" @@ -495,6 +498,7 @@ export class WEWWA { } OfflineHelper.reset().then(() => { localStorage.clear(); + // eslint-disable-next-line no-self-assign location = location; }); }); @@ -510,19 +514,18 @@ export class WEWWA { [W]eb
      [A]dapter

      - by hexxone + by hexxone `; - menu.append(preFoot, footer); + this.htmlMenu.append(preFoot, footer); } // eslint-disable-next-line valid-jsdoc /** - * Add Language Menu - * @ignore - */ + * Add Language Menu + * @ignore + */ private makeMenuLocalization(ce: (e: any) => any, lang, local, props) { - const self = this; // add html struct const row = ce("tr"); const td1 = ce("td"); @@ -557,7 +560,9 @@ export class WEWWA { } } // if changed, do it all over again. - lan.addEventListener("change", function (e) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + lan.addEventListener("change", function () { // eslint-disable-next-line no-invalid-this localStorage.setItem("wewwaLang", this.value); // eslint-disable-next-line no-invalid-this @@ -573,9 +578,9 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Add Audio Menu - * @ignore - */ + * Add Audio Menu + * @ignore + */ private addMenuAudio(ce: (e: any) => any, tblBody: any) { // audio input methods const row = ce("tr"); @@ -588,7 +593,7 @@ export class WEWWA { const aBtn1 = ce("a"); aBtn1.classList.add("audio"); aBtn1.innerHTML = "Microphone"; - aBtn1.addEventListener("click", (e) => { + aBtn1.addEventListener("click", () => { this.initMicrophone(); }); @@ -596,7 +601,7 @@ export class WEWWA { const aBtn2 = ce("a"); aBtn2.classList.add("audio"); aBtn2.innerHTML = "Desktop Audio (Chrome)"; - aBtn2.addEventListener("click", (e) => { + aBtn2.addEventListener("click", () => { this.initDesktop(); }); @@ -604,7 +609,7 @@ export class WEWWA { const aBtn3 = ce("a"); aBtn3.classList.add("audio"); aBtn3.innerHTML = "Select URL"; - aBtn3.addEventListener("click", (e) => { + aBtn3.addEventListener("click", () => { const uri = prompt( "Please enter some audio file URL\r\n\r\nYouTube, Soundcloud etc. ARE NOT YET SUPPORTED!", "https://example.com/test.mp3" @@ -667,7 +672,7 @@ export class WEWWA { const stopBtn = ce("a"); stopBtn.classList.add("red"); stopBtn.innerHTML = "Stop All Audio"; - stopBtn.addEventListener("click", (e) => { + stopBtn.addEventListener("click", () => { this.stopAudioInterval(); }); hrtd1.append(stopBtn); @@ -680,14 +685,10 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Add preview Image, Title and Link - * @ignore - */ - private addMenuHeader( - ce: (e: any) => any, - proj: any, - menu = this.htmlMenu - ) { + * Add preview Image, Title and Link + * @ignore + */ + private addMenuHeader(ce: (e: any) => any, proj: any, menu = this.htmlMenu) { const preview = ce("img"); preview.setAttribute("src", proj.preview); preview.setAttribute("alt", "Steam Workshop Preview Image"); @@ -700,7 +701,7 @@ export class WEWWA { link.setAttribute( "href", "https://steamcommunity.com/sharedfiles/filedetails/?id=" + - proj.workshopid + proj.workshopid ); link.setAttribute("target", "_blank"); link.innerHTML = "

      Open Workshop Page

      "; @@ -709,16 +710,21 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Create an HTML Menu Item from project json property - * @ignore - */ + * Create an HTML Menu Item from project json property + * @ignore + */ private createItem(prop, itm) { if (!itm.type || itm.type == "hidden") return null; + + // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; + const ce = (e) => document.createElement(e); + // table structure const row = ce("tr"); row.setAttribute("id", "wewwa_" + prop); + // Text const column1 = ce("td"); column1.classList.add("left"); @@ -734,84 +740,83 @@ export class WEWWA { // Process actual prop type switch (itm.type) { - // only text across 3 columns - case "text": - txt = ce("div"); - txt.innerHTML = itm.realText ? itm.realText : itm.text; - column1.setAttribute("colspan", 3); - break; + // only text across 3 columns + case "text": + txt = ce("div"); + txt.innerHTML = itm.realText ? itm.realText : itm.text; + column1.setAttribute("colspan", 3); + break; // combo select-box across 2 columns - case "combo": - inpt = ce("select"); - // set options - for (const o of itm.options) { - const opt = ce("option"); - opt.setAttribute("value", o.value); - opt.innerText = o.realLabel ? o.realLabel : o.label; - if (itm.value == o.value) - opt.setAttribute("selected", true); - inpt.appendChild(opt); - } - break; + case "combo": + inpt = ce("select"); + // set options + for (const o of itm.options) { + const opt = ce("option"); + opt.setAttribute("value", o.value); + opt.innerText = o.realLabel ? o.realLabel : o.label; + if (itm.value == o.value) opt.setAttribute("selected", true); + inpt.appendChild(opt); + } + break; // system color picker across 2 columns - case "color": - inpt = ce("input"); - inpt.setAttribute("type", "color"); - break; + case "color": + inpt = ce("input"); + inpt.setAttribute("type", "color"); + break; // Checkbox across 2 columns - case "bool": - inpt = ce("input"); - inpt.setAttribute("type", "checkbox"); - inpt.setAttribute("readonly", true); - break; + case "bool": + inpt = ce("input"); + inpt.setAttribute("type", "checkbox"); + inpt.setAttribute("readonly", true); + break; // Slider input across 1 column; + 1 column Up/Down - case "slider": { - const canEdit = itm.editable; - // create numeric-up-down - const sliderVal = ce(canEdit ? "input" : "output"); - sliderVal.name = "wewwa_out_" + prop; - sliderVal.setAttribute("id", sliderVal.name); - sliderVal.setAttribute("type", "number"); - sliderVal.style.width = "75%"; - if (canEdit) { - sliderVal.setAttribute("value", itm.value); - sliderVal.addEventListener("change", function (e) { - // eslint-disable-next-line no-invalid-this - self.setProperty(prop, this); - }); - } else { - sliderVal.innerHTML = itm.value; + case "slider": { + const canEdit = itm.editable; + // create numeric-up-down + const sliderVal = ce(canEdit ? "input" : "output"); + sliderVal.name = "wewwa_out_" + prop; + sliderVal.setAttribute("id", sliderVal.name); + sliderVal.setAttribute("type", "number"); + sliderVal.style.width = "75%"; + if (canEdit) { + sliderVal.setAttribute("value", itm.value); + sliderVal.addEventListener("change", function () { + // eslint-disable-next-line no-invalid-this + self.setProperty(prop, this); + }); + } else { + sliderVal.innerHTML = itm.value; + } + // create td3 + column3 = ce("td"); + column3.append(sliderVal); + // create actual slider & values + inpt = ce("input"); + inpt.setAttribute("type", "range"); + inpt.max = itm.max; + inpt.min = itm.min; + inpt.step = 0.1; + break; } - // create td3 - column3 = ce("td"); - column3.append(sliderVal); - // create actual slider & values - inpt = ce("input"); - inpt.setAttribute("type", "range"); - inpt.max = itm.max; - inpt.min = itm.min; - inpt.step = 0.1; - break; - } - // Text input across 2 columns - case "textinput": - inpt = ce("input"); - inpt.setAttribute("type", "text"); - break; + // Text input across 2 columns + case "textinput": + inpt = ce("input"); + inpt.setAttribute("type", "text"); + break; // File input across 2 columns - case "file": - inpt = ce("input"); - inpt.setAttribute("type", "file"); - break; - - default: - Smallog.error("unkown setting type: " + itm.type, LogHead); - break; + case "file": + inpt = ce("input"); + inpt.setAttribute("type", "file"); + break; + + default: + Smallog.error("unkown setting type: " + itm.type, LogHead); + break; } const eid = "wewwa_prop_" + prop; @@ -828,7 +833,7 @@ export class WEWWA { if (inpt) { inpt.style.width = "100%"; inpt.setAttribute("id", eid); - inpt.addEventListener("change", function (e) { + inpt.addEventListener("change", function () { // eslint-disable-next-line no-invalid-this self.setProperty(prop, this); }); @@ -849,10 +854,10 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Callback for UI-Settings changes - * Will apply them to the storage and running wallaper. - * @public - */ + * Callback for UI-Settings changes + * Will apply them to the storage and running wallaper. + * @public + */ public setProperty(prop, elm) { // get the type and apply the value const props = this.project.general.properties; @@ -876,46 +881,42 @@ export class WEWWA { // process value based on DOM element type switch (props[prop].type) { - case "bool": - applyCall(elm.checked == true); - break; - case "color": - applyCall(this.hexToRgb(elm.value)); - break; - case "file": - this.loadXHRSaveLocal(elm.value, (res) => applyCall(res)); - break; - case "slider": - if (elm.name.includes("_out_")) { - const inpt: any = document.querySelector("#wewwa_" + prop); - if (inpt) inpt.value = elm.value; - else Smallog.error("Slider not found: " + prop, LogHead); - } else { - const slide: any = document.querySelector( - "#wewwa_out_" + prop - ); - if (slide) slide.value = elm.value; - else - Smallog.error( - "Numericupdown not found: " + prop, - LogHead - ); - } - case "combo": - case "textinput": - applyCall(elm.value); - break; + case "bool": + applyCall(elm.checked == true); + break; + case "color": + applyCall(this.hexToRgb(elm.value)); + break; + case "file": + this.loadXHRSaveLocal(elm.value, (res) => applyCall(res)); + break; + case "slider": + if (elm.name.includes("_out_")) { + const inpt: any = document.querySelector("#wewwa_" + prop); + if (inpt) inpt.value = elm.value; + else Smallog.error("Slider not found: " + prop, LogHead); + } else { + const slide: any = document.querySelector("#wewwa_out_" + prop); + if (slide) slide.value = elm.value; + else Smallog.error("Numericupdown not found: " + prop, LogHead); + } + // eslint-disable-next-line no-fallthrough + case "combo": + case "textinput": + applyCall(elm.value); + break; } } /** - * will load the given file and return it as dataURL. - * this way we can easily store whole files in the configuration & localStorage. - * its not safe that this works with something else than image files. - * @param {string} url - * @param {function (data: (string | ArrayBuffer)): void} resCall - * @ignore - */ + * will load the given file and return it as dataURL. + * this way we can easily store whole files in the configuration & localStorage. + * its not safe that this works with something else than image files. + * @param {string} url file + * @param {function (data: (string | ArrayBuffer)): void} resCall finished-call + * @ignore + * @returns {void} + */ private loadXHRSaveLocal(url, resCall) { WascUtil.myFetch(url, "blob").then((resp) => { // Read out file contents as a Data URL @@ -928,9 +929,10 @@ export class WEWWA { } /** - * Show or hide menu items based on eval condition - * @public - */ + * Show or hide menu items based on eval condition + * @public + * @returns {void} + */ public evaluateSettings() { // dynamic prefix for evaluation const pre = "wewwaProps"; @@ -951,10 +953,10 @@ export class WEWWA { // loop all partial values of the check for (const part of partials) { let prefix = pre + "."; - const onlyVal = part.match(/[!a-zA-Z0-9_\.]*/)[0]; + const onlyVal = part.match(/[!a-zA-Z0-9_.]*/)[0]; if ( !onlyVal.startsWith(prefix) && - !onlyVal.startsWith("!" + prefix) + !onlyVal.startsWith("!" + prefix) ) { // fix for inverted values let replW = onlyVal; @@ -968,9 +970,7 @@ export class WEWWA { } try { visible = - new Function(pre, "return (" + cprop + ")")( - wewwaProps - ) === true; + new Function(pre, "return (" + cprop + ")")(wewwaProps) === true; } catch (e) { Smallog.error( "Error: (" + cprop + ") for: " + p + " => " + e, @@ -989,17 +989,17 @@ export class WEWWA { // set its value const elm: any = htElm.childNodes[1].childNodes[0]; switch (prop.type) { - case "color": - elm.value = this.rgbToHex(prop.value); - break; - case "bool": - elm.checked = prop.value == true; - break; - case "slider": - case "combo": - case "textinput": - elm.value = prop.value; - break; + case "color": + elm.value = this.rgbToHex(prop.value); + break; + case "bool": + elm.checked = prop.value == true; + break; + case "slider": + case "combo": + case "textinput": + elm.value = prop.value; + break; } } } @@ -1010,9 +1010,9 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Send one or more properties to the Wallpaper - * @public - */ + * Send one or more properties to the Wallpaper + * @public + */ public applyProp(prop) { const wpl = window["wallpaperPropertyListener"]; if (wpl && wpl.applyUserProperties) { @@ -1022,9 +1022,9 @@ export class WEWWA { // eslint-disable-next-line valid-jsdoc /** - * Send paused-status to the Wallpaper - * @public - */ + * Send paused-status to the Wallpaper + * @public + */ public setPaused(val: boolean) { const wpl = window["wallpaperPropertyListener"]; if (this.isPaused == val) return; @@ -1055,10 +1055,10 @@ export class WEWWA { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? [ - parseInt(result[1], 16) / 255, - parseInt(result[2], 16) / 255, - parseInt(result[3], 16) / 255, - ].join(" ") + parseInt(result[1], 16) / 255, + parseInt(result[2], 16) / 255, + parseInt(result[3], 16) / 255, + ].join(" ") : null; } @@ -1067,9 +1067,10 @@ export class WEWWA { // ------------------------------------- /** - * Request microphone from browser - * @ignore - */ + * Request microphone from browser + * @ignore + * @returns {void} + */ private initMicrophone() { const md = navigator.mediaDevices as any; if (!md["getUserMedia"]) return; @@ -1098,8 +1099,9 @@ export class WEWWA { } /** - * Initiate Desktop auddio streaming - */ + * Initiate Desktop auddio streaming + * @returns {void} + */ private async initDesktop() { const md = navigator.mediaDevices as any; if (!md["getDisplayMedia"]) return; @@ -1119,19 +1121,23 @@ export class WEWWA { }); } - // eslint-disable-next-line valid-jsdoc /** - * Start the audio processing & analyzer - * @ignore - */ - private initFile(file) { + * Start the audio processing & analyzer + * @param {Blob | MediaSource | string} source start audio + * @ignore + * @returns {void} + */ + private initFile(source: Blob | MediaSource | string) { // stop previous analyzer this.stopAudioInterval(); - if (!file) return; + if (!source) return; // create player this.audio = document.createElement("audio"); - this.audio.src = file.name ? URL.createObjectURL(file) : file; + this.audio.src = + source instanceof String + ? (source as string) + : URL.createObjectURL(source as any); this.audio.autoplay = true; this.audio.setAttribute("controls", "true"); this.audio.play(); @@ -1143,29 +1149,30 @@ export class WEWWA { } /** - * - * @param {MediaStream} src - */ - private makeAnalyzer(src: MediaStream | HTMLAudioElement) { + * Create actual HTML5 audio analyzer + * @param {MediaStream | HTMLAudioElement} source start audio + * @returns {void} + */ + private makeAnalyzer(source: MediaStream | HTMLAudioElement) { // new context this.ctx = new (window.AudioContext || window["webkitAudioContext"])({ sampleRate: 48000, }); // microphone or desktop stream sauce - if (src instanceof MediaStream) { - this.source = this.ctx.createMediaStreamSource(src); + if (source instanceof MediaStream) { + this.source = this.ctx.createMediaStreamSource(source); // hack for firefox to keep stream running - window["persistAudioStream"] = src; + window["persistAudioStream"] = source; } // audio html element sauce - if (src instanceof HTMLAudioElement) { - this.source = this.ctx.createMediaElementSource(src); + if (source instanceof HTMLAudioElement) { + this.source = this.ctx.createMediaElementSource(source); // we want to hear this on our pc => connect source OUT to media IN this.source.connect(this.ctx.destination); } // new analyzer this.analyser = this.ctx.createAnalyser(); - this.analyser.smoothingTimeConstant = 0.05; + this.analyser.smoothingTimeConstant = 0.02; this.analyser.fftSize = 256; // connect source OUT to analyzer IN this.source.connect(this.analyser); @@ -1174,9 +1181,10 @@ export class WEWWA { } /** - * Start the processing loop - * @ignore - */ + * Start the processing loop + * @ignore + * @returns {void} + */ private startAudioInterval() { const data = new Uint8Array(128); // 33ms ~~ 30fps @@ -1194,14 +1202,15 @@ export class WEWWA { this.applyProp({ audioprocessing: { value: true } }); } - // eslint-disable-next-line valid-jsdoc /** - * html5 audio analyser gives us mono data from 0(bass) to 128(treble) - * however, wallpaper engine expects stereo data in following format: - * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) - * so we do some array transformation... and divide by 255 (8bit-uint becomes float) - * @ignore - */ + * html5 audio analyser gives us mono data from 0(bass) to 128(treble) + * however, wallpaper engine expects stereo data in following format: + * 0(L: low) to 63(L: treble) and 64(R: low) to 128(R: treble) + * so we do some array transformation... and divide by 255 (8bit-uint becomes float) + * @ignore + * @param {Uint8Array} data input + * @returns {number[]} result + */ private convertAudio(data: Uint8Array) { const stereo = []; let sIdx = 0; @@ -1213,9 +1222,10 @@ export class WEWWA { } /** - * Stop the processing loop - * @public - */ + * Stop the processing loop + * @public + * @returns {void} + */ public stopAudioInterval() { window["persistAudioStream"] = null; document.getElementById("wewwaAudioInput").setAttribute("value", ""); diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 82c035a..8662867 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -1,44 +1,44 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ -import {CSettings, CComponent, waitReady} from './'; +import { CSettings, CComponent, waitReady } from "./"; -const ELM_ID = 'triggerwarn'; -const IMG_SRC = './img/triggerwarn.png'; +const ELM_ID = "triggerwarn"; +const IMG_SRC = "./img/triggerwarn.png"; /** -* Seizure display warnings -* @public -* @extends {CSettings} -*/ + * Seizure display warnings + * @public + * @extends {CSettings} + */ class WarnSettings extends CSettings { - seizure_warning: boolean = true; - animate_seconds: number = 2; - wait_seconds: number = 6; + seizure_warning = true; + animate_seconds = 2; + wait_seconds = 6; } /** -* Displays a seizure warning image centered on html for a given Time. -* @public -* @extends {CComponent} -*/ + * Displays a seizure warning image centered on html for a given Time. + * @public + * @extends {CComponent} + */ export class WarnHelper extends CComponent { /* - * @public - */ + * @public + */ public settings: WarnSettings = new WarnSettings(); private element: HTMLDivElement; /** - * Create and prepare once document ready - */ + * Create and prepare once document ready + */ constructor() { super(); @@ -49,11 +49,12 @@ export class WarnHelper extends CComponent { } /** - * Make custom style - * @ignore - */ + * Make custom style + * @ignore + * @returns {void} + */ private injectCSS() { - const st = document.createElement('style'); + const st = document.createElement("style"); st.innerHTML = ` #${ELM_ID} { opacity: 0; @@ -71,22 +72,23 @@ export class WarnHelper extends CComponent { } /** - * Make custom html - * @ignore - */ + * Make custom html + * @ignore + * @returns {void} + */ private injectHTML() { - this.element = document.createElement('img'); + this.element = document.createElement("img"); this.element.id = ELM_ID; - this.element.setAttribute('src', IMG_SRC); - this.element.setAttribute('alt', 'Seizure Warning'); + this.element.setAttribute("src", IMG_SRC); + this.element.setAttribute("alt", "Seizure Warning"); document.body.append(this.element); } /** - * Show the warning - * @public - * @return {Promise} hidden again - */ + * Show the warning + * @public + * @return {Promise} hidden again + */ public show(): Promise { return new Promise((resolve) => { // dont show @@ -95,7 +97,7 @@ export class WarnHelper extends CComponent { return; } // show it - this.element.classList.add('show'); + this.element.classList.add("show"); // wait some time setTimeout(() => { this.hide().then(() => { @@ -106,14 +108,14 @@ export class WarnHelper extends CComponent { } /** - * Hide warning - * @public - * @return {Promise} hidden - */ + * Hide warning + * @public + * @return {Promise} hidden + */ public hide(): Promise { return new Promise((resolve) => { // hide it & wait - this.element.classList.remove('show'); + this.element.classList.remove("show"); setTimeout(() => { resolve(); }, this.settings.animate_seconds * 1000); @@ -121,16 +123,19 @@ export class WarnHelper extends CComponent { } /** - * Settings have been changed - * @public - * @return {Promise} finished - */ + * Settings have been changed + * @public + * @return {Promise} finished + */ public updateSettings(): Promise { // fix for instantly removing the warning while it shows - if (!this.settings.seizure_warning && this.element.classList.contains('show')) { + if ( + !this.settings.seizure_warning && + this.element.classList.contains("show") + ) { this.hide(); } // whatever return Promise.resolve(); } -}; +} diff --git a/src/XRHelper.ts b/src/XRHelper.ts index badda37..a6ed7b3 100644 --- a/src/XRHelper.ts +++ b/src/XRHelper.ts @@ -1,27 +1,26 @@ /** -* @author hexxone / https://hexx.one -* @author mrdoob / http://mrdoob.com -* @author Mugen87 / https://github.com/Mugen87 -*/ - -import {CSettings, CComponent, waitReady} from './'; -import {XRSessionInit} from './XRWebGL'; + * @author hexxone / https://hexx.one + * @author mrdoob / http://mrdoob.com + * @author Mugen87 / https://github.com/Mugen87 + */ +import { CSettings, CComponent, waitReady } from "./"; +import { XRSessionInit } from "./XRWebGL"; /** * XR Settings * @extends {CSettings} */ export class XRSettings extends CSettings { - private xr_mode: boolean = false; + private xr_mode = false; } /** -* XR / VR / AR Helper class. -* Provides availability information and starts/stops a three-js XR session -* @public -* @extends {CComponent} -*/ + * XR / VR / AR Helper class. + * Provides availability information and starts/stops a three-js XR session + * @public + * @extends {CComponent} + */ export class XRHelper extends CComponent { public settings: XRSettings = new XRSettings(); @@ -30,8 +29,8 @@ export class XRHelper extends CComponent { private currentSession: XRSession; /** - * Get typed navigator - */ + * Get typed navigator + */ constructor() { super(); waitReady().then(() => { @@ -41,113 +40,129 @@ export class XRHelper extends CComponent { } /** - * Create the "Exit" Button - */ + * Create the "Exit" Button + * @returns {void} + */ private createBtn() { - const btn = this.button = document.createElement('button'); + const btn = (this.button = document.createElement("button")); btn.disabled = true; - btn.style.display = 'none'; - btn.style.position = 'absolute'; - btn.style.bottom = '50px'; - btn.style.padding = '12px 6px'; - btn.style.border = '1px solid #fff'; - btn.style.borderRadius = '4px'; - btn.style.background = 'rgba(0,0,0,0.1)'; - btn.style.color = '#fff'; - btn.style.font = 'normal 13px sans-serif'; - btn.style.textAlign = 'center'; - btn.style.opacity = '0.5'; - btn.style.outline = 'none'; - btn.style.zIndex = '99999'; + btn.style.display = "none"; + btn.style.position = "absolute"; + btn.style.bottom = "50px"; + btn.style.padding = "12px 6px"; + btn.style.border = "1px solid #fff"; + btn.style.borderRadius = "4px"; + btn.style.background = "rgba(0,0,0,0.1)"; + btn.style.color = "#fff"; + btn.style.font = "normal 13px sans-serif"; + btn.style.textAlign = "center"; + btn.style.opacity = "0.5"; + btn.style.outline = "none"; + btn.style.zIndex = "99999"; // auto center horizontally - btn.style.left = '50%'; - btn.style.transform = 'translate(-50%, 0)'; + btn.style.left = "50%"; + btn.style.transform = "translate(-50%, 0)"; btn.onmouseenter = () => { - btn.style.opacity = '1.0'; + btn.style.opacity = "1.0"; }; btn.onmouseleave = () => { - btn.style.opacity = '0.5'; + btn.style.opacity = "0.5"; }; document.body.append(btn); } /** - * @return {boolean} whether XR is supported and available or not - */ + * @return {boolean} whether XR is supported and available or not + */ private async isSupported() { - if ('xr' in this.nav) { - return (await this.nav.xr.isSessionSupported('immersive-vr')); + if ("xr" in this.nav) { + return await this.nav.xr.isSessionSupported("immersive-vr"); } return false; } /** * Trys to start a Web-XR session. - * if successfull, will provide functionality for leaving web-XR again. - * @param {function (params:XRSession): void} sessionCallback - * @return {Promise} + * if successfull, will provide functionality for leaving web-XR again. + * @param {function (params:XRSession): void} sessionCallback callback to start XR + * @return {Promise} success if VR is available */ - public async enableSession(sessionCallback: (xrs: XRSession) => void): Promise { - return new Promise(async (resolve) => { + public async enableSession( + sessionCallback: (xrs: XRSession) => void + ): Promise { + return new Promise((resolve) => // check availability - const avail = await this.isSupported(); - if (!avail) { - if (window.isSecureContext === false && confirm('WebXR may need HTTPS to function. Redirect?')) { - document.location.href = document.location.href.replace(/^http:/, 'https:'); + this.isSupported().then((avail) => { + if (avail) { + // enable "Enter" button + this.button.textContent = "Enter XR"; + this.button.style.display = "block"; + this.button.disabled = false; + + // "Toggle" style event listener + this.button.addEventListener("click", () => { + if (!avail) return; + // end previous session + if (this.currentSession) { + this.currentSession.end(); + return; + } + + // WebXR's requestReferenceSpace only works if the corresponding feature + // was requested at session creation time. For simplicity, just ask for + // the interesting ones as optional features, but be aware that the + // requestReferenceSpace call will fail if it turns out to be unavailable. + // ('local' is always available for immersive sessions and doesn't need to + // be requested separately.) + const sessionInit: XRSessionInit = { + optionalFeatures: ["local-floor"], + }; + this.nav.xr.requestSession("immersive-vr", sessionInit).then( + (sess) => { + this.currentSession = sess; + + const lstnr = (/* event*/) => { + this.currentSession.removeEventListener("end", lstnr); + this.currentSession = null; + this.button.textContent = "Enter VR"; + sessionCallback(null); + }; + sess.addEventListener("end", lstnr); + // show exit button + this.button.textContent = "Exit VR"; + sessionCallback(sess); + }, + (reason) => { + console.error( + "[WEBXR] RequestSession failed! Reason: " + reason + ); + } + ); + }); } else { - this.button.textContent = 'VR not available!'; - this.button.style.display = 'block'; - console.error('[WEBXR] Not avaiable! More info: https://immersiveweb.dev/'); - } - resolve(false); - return; - } - - this.button.textContent = 'Enter XR'; - this.button.style.display = 'block'; - this.button.disabled = false; - - // "Toggle" style event listener - this.button.addEventListener('click', async () => { - if (!avail) return; - // end previous session - if (this.currentSession) { - await this.currentSession.end(); + // WebXR is not available... + // might be because of missing https? + if ( + window.isSecureContext === false && + confirm("WebXR may need HTTPS to function. Redirect?") + ) { + document.location.href = document.location.href.replace( + /^http:/, + "https:" + ); + } else { + this.button.textContent = "VR not available!"; + this.button.style.display = "block"; + console.error( + "[WEBXR] Not avaiable! More info: https://immersiveweb.dev/" + ); + } return; } - - // WebXR's requestReferenceSpace only works if the corresponding feature - // was requested at session creation time. For simplicity, just ask for - // the interesting ones as optional features, but be aware that the - // requestReferenceSpace call will fail if it turns out to be unavailable. - // ('local' is always available for immersive sessions and doesn't need to - // be requested separately.) - const sessionInit: XRSessionInit = { - optionalFeatures: ['local-floor'], - }; - this.nav.xr.requestSession('immersive-vr', sessionInit).then((sess) => { - this.currentSession = sess; - - const lstnr = (/* event*/) => { - this.currentSession.removeEventListener('end', lstnr); - this.currentSession = null; - this.button.textContent = 'Enter VR'; - sessionCallback(null); - }; - sess.addEventListener('end', lstnr); - // show exit button - this.button.textContent = 'Exit VR'; - sessionCallback(sess); - }, - (reason) => { - console.error('[WEBXR] RequestSession failed! Reason: ' + reason); - }); - }); - - // success - resolve(true); - }); + resolve(avail); + }) + ); } -}; +} diff --git a/src/XRWebGL.d.ts b/src/XRWebGL.d.ts index 9d55d68..dd10077 100644 --- a/src/XRWebGL.d.ts +++ b/src/XRWebGL.d.ts @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/consistent-type-definitions */ + /* eslint-disable no-unused-vars */ + // # General utilities type EventHandlerNonNull = (event: Event) => void; @@ -7,7 +9,9 @@ type EventHandlerNonNull = (event: Event) => void; type EventHandler = EventHandlerNonNull | null | undefined; // DOMString is not exactly a string (https://heycam.github.io/webidl/#idl-DOMString). + // (TODO) + type DOMString = string; // # WebXR @@ -15,11 +19,11 @@ type DOMString = string; export type XRSessionMode = "inline" | "immersive-vr" | "immersive-ar"; export type XRReferenceSpaceType = - | "viewer" - | "local" - | "local-floor" - | "bounded-floor" - | "unbounded"; + | "viewer" + | "local" + | "local-floor" + | "bounded-floor" + | "unbounded"; export type XRVisibilityState = "visible" | "visible-blurred" | "hidden"; @@ -30,270 +34,352 @@ export type XRHandedness = "none" | "left" | "right"; export type XRTargetRayMode = "gaze" | "tracked-pointer" | "screen"; export type XRWebGLRenderingContext = - | WebGLRenderingContext - | WebGL2RenderingContext; + | WebGLRenderingContext + | WebGL2RenderingContext; export interface XRSessionInit { - requiredFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) - optionalFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) + requiredFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) + + optionalFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) } declare global { - export class XRRigidTransform { - constructor(position?: DOMPointInit, orientation?: DOMPointInit); - readonly position: DOMPointReadOnly; - readonly orientation: DOMPointReadOnly; - readonly matrix: Float32Array; - readonly inverse: XRRigidTransform; - } - - export interface XRViewport { - readonly x: number; - readonly y: number; - readonly width: number; - readonly height: number; - } + export class XRRigidTransform { + constructor(position?: DOMPointInit, orientation?: DOMPointInit); + + readonly position: DOMPointReadOnly; + + readonly orientation: DOMPointReadOnly; + + readonly matrix: Float32Array; + + readonly inverse: XRRigidTransform; + } + + export interface XRViewport { + readonly x: number; + + readonly y: number; + + readonly width: number; + + readonly height: number; + } } // ## Events export interface XRSessionEventInit extends EventInit { - session: XRSession; + session: XRSession; } export interface XRInputSourceEventInit extends EventInit { - frame: XRFrame; - inputSource: XRInputSource; + frame: XRFrame; + + inputSource: XRInputSource; } export interface XRInputSourcesChangeEventInit extends EventInit { - session: XRSession; - added: XRInputSource[]; // FrozenArray (TODO?) - removed: XRInputSource[]; // FrozenArray (TODO?) + session: XRSession; + + added: XRInputSource[]; // FrozenArray (TODO?) + + removed: XRInputSource[]; // FrozenArray (TODO?) } export interface XRReferenceSpaceEventInit extends EventInit { - referenceSpace: XRReferenceSpace; - transform?: XRRigidTransform | null; + referenceSpace: XRReferenceSpace; + + transform?: XRRigidTransform | null; } declare global { - export class XRSessionEvent extends Event { - constructor(type: DOMString, eventInitDict: XRSessionEventInit); - readonly session: XRSession; - } - - export class XRInputSourceEvent extends Event { - constructor(type: DOMString, eventInitDict: XRInputSourceEventInit); - readonly frame: XRFrame; - readonly inputSource: XRInputSource; - } - export class XRInputSourcesChangeEvent extends Event { - constructor( - type: DOMString, - eventInitDict: XRInputSourcesChangeEventInit - ); - readonly session: XRSession; - readonly added: XRInputSource[]; // FrozenArray (TODO?) - readonly removed: XRInputSource[]; // FrozenArray (TODO?) - } - - export class XRReferenceSpaceEvent extends Event { - constructor(type: DOMString, eventInitDict: XRReferenceSpaceEventInit); - readonly referenceSpace: XRReferenceSpace; - readonly transform: XRRigidTransform | null; - } + export class XRSessionEvent extends Event { + constructor(type: DOMString, eventInitDict: XRSessionEventInit); + + readonly session: XRSession; + } + + export class XRInputSourceEvent extends Event { + constructor(type: DOMString, eventInitDict: XRInputSourceEventInit); + + readonly frame: XRFrame; + + readonly inputSource: XRInputSource; + } + + export class XRInputSourcesChangeEvent extends Event { + constructor(type: DOMString, eventInitDict: XRInputSourcesChangeEventInit); + + readonly session: XRSession; + + readonly added: XRInputSource[]; // FrozenArray (TODO?) + + readonly removed: XRInputSource[]; // FrozenArray (TODO?) + } + + export class XRReferenceSpaceEvent extends Event { + constructor(type: DOMString, eventInitDict: XRReferenceSpaceEventInit); + + readonly referenceSpace: XRReferenceSpace; + + readonly transform: XRRigidTransform | null; + } } // ## Permissions export interface XRPermissionDescriptor extends PermissionDescriptor { - mode?: XRSessionMode; - requiredFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) - optionalFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) + mode?: XRSessionMode; + + requiredFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) + + optionalFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) } declare global { - export interface XRPermissionStatus extends PermissionStatus { - granted: []; // FrozenArray (TODO?) ; (TODO) Also is this really `any`? - } - - // ## Input sources - - export interface XRInputSource { - readonly handedness: XRHandedness; - readonly targetRayMode: XRTargetRayMode; - readonly targetRaySpace: XRSpace; - readonly gripSpace: XRSpace | null; - readonly profiles: DOMString[]; // FrozenArray in the doc (TODO?) - } - - // This is actually a novel data structure which emulates a JS array (e.g. getter + `.length`) - // but it is not an array (TODO) - export type XRInputSourceArray = XRInputSource[]; - - // ## View - - export interface XRView { - readonly eye: XREye; - readonly projectionMatrix: Float32Array; - readonly transform: XRRigidTransform; - } - - // ## Spaces - - // eslint-disable-next-line @typescript-eslint/no-empty-interface - export interface XRSpace extends EventTarget {} - - export interface XRReferenceSpace extends XRSpace { - getOffsetReferenceSpace( - originOffset: XRRigidTransform - ): XRReferenceSpace; - onreset: EventHandler; - } - - export interface XRBoundedReferenceSpace extends XRReferenceSpace { - readonly boundsGeometry: DOMPointReadOnly[]; // FrozenArray (TODO?) - } - - // ## Poses - - export interface XRPose { - readonly transform: XRRigidTransform; - readonly emulatedPosition: boolean; - } - - export interface XRViewerPose extends XRPose { - readonly views: XRView[]; // FrozenArray in the docs (TODO?) - } - - // ## Frames - - export interface XRFrame { - readonly session: XRSession; - getViewerPose(referenceSpace: XRReferenceSpace): XRViewerPose | null; - getPose(space: XRSpace, baseSpace: XRSpace): XRPose | null; - } + export interface XRPermissionStatus extends PermissionStatus { + granted: []; // FrozenArray (TODO?) ; (TODO) Also is this really `any`? + } + + // ## Input sources + + export interface XRInputSource { + readonly handedness: XRHandedness; + + readonly targetRayMode: XRTargetRayMode; + + readonly targetRaySpace: XRSpace; + + readonly gripSpace: XRSpace | null; + + readonly profiles: DOMString[]; // FrozenArray in the doc (TODO?) + } + + // This is actually a novel data structure which emulates a JS array (e.g. getter + `.length`) + + // but it is not an array (TODO) + + export type XRInputSourceArray = XRInputSource[]; + + // ## View + + export interface XRView { + readonly eye: XREye; + + readonly projectionMatrix: Float32Array; + + readonly transform: XRRigidTransform; + } + + // ## Spaces + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + export interface XRSpace extends EventTarget {} + + export interface XRReferenceSpace extends XRSpace { + getOffsetReferenceSpace(originOffset: XRRigidTransform): XRReferenceSpace; + + onreset: EventHandler; + } + + export interface XRBoundedReferenceSpace extends XRReferenceSpace { + readonly boundsGeometry: DOMPointReadOnly[]; // FrozenArray (TODO?) + } + + // ## Poses + + export interface XRPose { + readonly transform: XRRigidTransform; + + readonly emulatedPosition: boolean; + } + + export interface XRViewerPose extends XRPose { + readonly views: XRView[]; // FrozenArray in the docs (TODO?) + } + + // ## Frames + + export interface XRFrame { + readonly session: XRSession; + + getViewerPose(referenceSpace: XRReferenceSpace): XRViewerPose | null; + + getPose(space: XRSpace, baseSpace: XRSpace): XRPose | null; + } } export type XRFrameRequestCallback = ( - time: DOMHighResTimeStamp, - frame: XRFrame + time: DOMHighResTimeStamp, + + frame: XRFrame ) => void; // ## WebGL interop export interface XRWebGLLayerInit { - antialias?: boolean; - depth?: boolean; - stencil?: boolean; - alpha?: boolean; - ignoreDepthValues?: boolean; - framebufferScaleFactor?: number; + antialias?: boolean; + + depth?: boolean; + + stencil?: boolean; + + alpha?: boolean; + + ignoreDepthValues?: boolean; + + framebufferScaleFactor?: number; } declare global { - export class XRWebGLLayer { - constructor( - session: XRSession, - context: XRWebGLRenderingContext, - layerInit?: XRWebGLLayerInit - ); - readonly antialias: boolean; - readonly ignoreDepthValues: boolean; - readonly framebuffer?: WebGLFramebuffer; - readonly framebufferWidth: number; - readonly framebufferHeight: number; - - getViewport(view: XRView): XRViewport | null; - static getNativeFramebufferScaleFactor(session: XRSession): number; - } + export class XRWebGLLayer { + constructor( + session: XRSession, + + context: XRWebGLRenderingContext, + + layerInit?: XRWebGLLayerInit + ); + + readonly antialias: boolean; + + readonly ignoreDepthValues: boolean; + + readonly framebuffer?: WebGLFramebuffer; + + readonly framebufferWidth: number; + + readonly framebufferHeight: number; + + getViewport(view: XRView): XRViewport | null; + + static getNativeFramebufferScaleFactor(session: XRSession): number; + } } // ## Session export interface XRRenderStateInit { - depthNear?: number; - depthFar?: number; - inlineVerticalFieldOfView?: number; - baseLayer?: XRWebGLLayer | null; + depthNear?: number; + + depthFar?: number; + + inlineVerticalFieldOfView?: number; + + baseLayer?: XRWebGLLayer | null; } declare global { - export interface XRRenderState { - readonly depthNear: number; - readonly depthFar: number; - readonly inlineVerticalFieldOfView?: number; - readonly baseLayer?: XRWebGLLayer; - } - - export interface XRSession extends EventTarget { - readonly visibilityState: XRVisibilityState; - readonly renderState: XRRenderState; - readonly inputSources: XRInputSourceArray; - - // Methods - updateRenderState(state?: XRRenderStateInit): void; - requestReferenceSpace( - type: XRReferenceSpaceType - ): Promise; - - requestAnimationFrame(callback: XRFrameRequestCallback): number; - cancelAnimationFrame(handle: number): void; - - end(): Promise; - - environmentBlendMode: string; - - // Events - onend: EventHandler; - oninputsourceschange: EventHandler; - onselect: EventHandler; - onselectstart: EventHandler; - onselectend: EventHandler; - onsqueeze: EventHandler; - onsqueezestart: EventHandler; - onsqueezeend: EventHandler; - onvisibilitychange: EventHandler; - } - - // ## System - - export interface XRSystem extends EventTarget { - isSessionSupported(mode: XRSessionMode): Promise; - requestSession( - mode: XRSessionMode, - options?: XRSessionInit - ): Promise; - ondevicechange: EventHandler; - addEventListener( - type: "devicechange", - listener: EventListenerOrEventListenerObject, - options?: boolean | AddEventListenerOptions - ): void; - } - - // ## Updates to existing objects - interface Navigator { - /** - * Optional because WebXR support is limited across browsers - */ - xr?: XRSystem; - } - - interface GLESRenderingContext { - /** - * Optional because WebXR support is limited across browsers - */ - makeXRCompatible?(): Promise; - } - - interface Window { - XRRigidTransform?: typeof XRRigidTransform; - XRWebGLLayer?: typeof XRWebGLLayer; - XRSessionEvent?: typeof XRSessionEvent; - XRInputSourceEvent?: typeof XRInputSourceEvent; - XRInputSourcesChangeEvent?: typeof XRInputSourcesChangeEvent; - XRReferenceSpaceEvent?: typeof XRReferenceSpaceEvent; - } + export interface XRRenderState { + readonly depthNear: number; + + readonly depthFar: number; + + readonly inlineVerticalFieldOfView?: number; + + readonly baseLayer?: XRWebGLLayer; + } + + export interface XRSession extends EventTarget { + readonly visibilityState: XRVisibilityState; + + readonly renderState: XRRenderState; + + readonly inputSources: XRInputSourceArray; + + // Methods + + updateRenderState(state?: XRRenderStateInit): void; + + requestReferenceSpace( + type: XRReferenceSpaceType + ): Promise; + + requestAnimationFrame(callback: XRFrameRequestCallback): number; + + cancelAnimationFrame(handle: number): void; + + end(): Promise; + + environmentBlendMode: string; + + // Events + + onend: EventHandler; + + oninputsourceschange: EventHandler; + + onselect: EventHandler; + + onselectstart: EventHandler; + + onselectend: EventHandler; + + onsqueeze: EventHandler; + + onsqueezestart: EventHandler; + + onsqueezeend: EventHandler; + + onvisibilitychange: EventHandler; + } + + // ## System + + export interface XRSystem extends EventTarget { + isSessionSupported(mode: XRSessionMode): Promise; + + requestSession( + mode: XRSessionMode, + + options?: XRSessionInit + ): Promise; + + ondevicechange: EventHandler; + + addEventListener( + type: "devicechange", + + listener: EventListenerOrEventListenerObject, + + options?: boolean | AddEventListenerOptions + ): void; + } + + // ## Updates to existing objects + + interface Navigator { + /** + + * Optional because WebXR support is limited across browsers + + */ + + xr?: XRSystem; + } + + interface GLESRenderingContext { + /** + + * Optional because WebXR support is limited across browsers + + */ + + makeXRCompatible?(): Promise; + } + + interface Window { + XRRigidTransform?: typeof XRRigidTransform; + + XRWebGLLayer?: typeof XRWebGLLayer; + + XRSessionEvent?: typeof XRSessionEvent; + + XRInputSourceEvent?: typeof XRInputSourceEvent; + + XRInputSourcesChangeEvent?: typeof XRInputSourcesChangeEvent; + + XRReferenceSpaceEvent?: typeof XRReferenceSpaceEvent; + } } diff --git a/src/gles.d.ts b/src/gles.d.ts index 27c714e..440c076 100644 --- a/src/gles.d.ts +++ b/src/gles.d.ts @@ -1,10 +1,12 @@ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ +/* eslint-disable @typescript-eslint/no-empty-interface */ /* eslint-disable no-unused-vars */ /** * Interface for GL-ES3 Typization * (yet to be implemented in ts-spec) */ -interface GLESObject { } +interface GLESObject {} interface GLESActiveInfo { readonly name: string; @@ -12,15 +14,15 @@ interface GLESActiveInfo { readonly type: number; } -interface GLESBuffer extends GLESObject { } +interface GLESBuffer extends GLESObject {} -interface GLESFramebuffer extends GLESObject { } +interface GLESFramebuffer extends GLESObject {} -interface GLESProgram extends GLESObject { } +interface GLESProgram extends GLESObject {} -interface GLESRenderbuffer extends GLESObject { } +interface GLESRenderbuffer extends GLESObject {} -interface GLESShader extends GLESObject { } +interface GLESShader extends GLESObject {} interface GLESShaderPrecisionFormat { readonly precision: number; @@ -28,14 +30,18 @@ interface GLESShaderPrecisionFormat { readonly rangeMin: number; } -interface GLESTexture extends GLESObject { } +interface GLESTexture extends GLESObject {} -interface GLESUniformLocation { } +interface GLESUniformLocation {} declare interface GLESRenderingContext extends WebGL2RenderingContext { activeTexture(texture: number): void; attachShader(program: GLESProgram | null, shader: GLESShader | null): void; - bindAttribLocation(program: GLESProgram | null, index: number, name: string): void; + bindAttribLocation( + program: GLESProgram | null, + index: number, + name: string + ): void; bindBuffer(target: number, buffer: GLESBuffer | null): void; bindFramebuffer(target: number, framebuffer: GLESFramebuffer | null): void; bindRenderbuffer(target: number, renderbuffer: GLESRenderbuffer | null): void; @@ -44,9 +50,22 @@ declare interface GLESRenderingContext extends WebGL2RenderingContext { blendEquation(mode: number): void; blendEquationSeparate(modeRGB: number, modeAlpha: number): void; blendFunc(sfactor: number, dfactor: number): void; - blendFuncSeparate(srcRGB: number, dstRGB: number, srcAlpha: number, dstAlpha: number): void; - bufferData(target: number, size: number | ArrayBufferView | ArrayBuffer, usage: number): void; - bufferSubData(target: number, offset: number, data: ArrayBufferView | ArrayBuffer): void; + blendFuncSeparate( + srcRGB: number, + dstRGB: number, + srcAlpha: number, + dstAlpha: number + ): void; + bufferData( + target: number, + size: number | ArrayBufferView | ArrayBuffer, + usage: number + ): void; + bufferSubData( + target: number, + offset: number, + data: ArrayBufferView | ArrayBuffer + ): void; checkFramebufferStatus(target: number): number; clear(mask: number): void; clearColor(red: number, green: number, blue: number, alpha: number): void; @@ -56,8 +75,26 @@ declare interface GLESRenderingContext extends WebGL2RenderingContext { compileShader(shader: GLESShader | null): void; // compressedTexImage2D(target: number, level: number, internalformat: number, width: number, height: number, border: number, data: ArrayBufferView | ArrayBuffer): void; // compressedTexSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, data: ArrayBufferView | ArrayBuffer): void; - copyTexImage2D(target: number, level: number, internalformat: number, x: number, y: number, width: number, height: number, border: number): void; - copyTexSubImage2D(target: number, level: number, xoffset: number, yoffset: number, x: number, y: number, width: number, height: number): void; + copyTexImage2D( + target: number, + level: number, + internalformat: number, + x: number, + y: number, + width: number, + height: number, + border: number + ): void; + copyTexSubImage2D( + target: number, + level: number, + xoffset: number, + yoffset: number, + x: number, + y: number, + width: number, + height: number + ): void; createBuffer(): GLESBuffer | null; createFramebuffer(): GLESFramebuffer | null; createProgram(): GLESProgram | null; @@ -83,33 +120,69 @@ declare interface GLESRenderingContext extends WebGL2RenderingContext { enableVertexAttribArray(index: number): void; finish(): void; flush(): void; - framebufferRenderbuffer(target: number, attachment: number, renderbuffertarget: number, renderbuffer: GLESRenderbuffer | null): void; - framebufferTexture2D(target: number, attachment: number, textarget: number, texture: GLESTexture | null, level: number): void; + framebufferRenderbuffer( + target: number, + attachment: number, + renderbuffertarget: number, + renderbuffer: GLESRenderbuffer | null + ): void; + framebufferTexture2D( + target: number, + attachment: number, + textarget: number, + texture: GLESTexture | null, + level: number + ): void; frontFace(mode: number): void; generateMipmap(target: number): void; - getActiveAttrib(program: GLESProgram | null, index: number): GLESActiveInfo | null; - getActiveUniform(program: GLESProgram | null, index: number): GLESActiveInfo | null; + getActiveAttrib( + program: GLESProgram | null, + index: number + ): GLESActiveInfo | null; + getActiveUniform( + program: GLESProgram | null, + index: number + ): GLESActiveInfo | null; getAttachedShaders(program: GLESProgram | null): GLESShader[] | null; getAttribLocation(program: GLESProgram | null, name: string): number; getBooleanv(pname: number): boolean; getBufferParameteriv(target: number, pname: number): number; getError(): number; getFloatv(pname: number): number; - getFramebufferAttachmentParameteriv(target: number, attachment: number, pname: number): number; + getFramebufferAttachmentParameteriv( + target: number, + attachment: number, + pname: number + ): number; getIntegerv(pname: number): number; getProgramiv(program: GLESProgram, pname: number): number; getProgramInfoLog(program: GLESProgram | null): string | null; - getRenderbufferParameteriv(program: GLESProgram | null, pname: number): number; + getRenderbufferParameteriv( + program: GLESProgram | null, + pname: number + ): number; getShaderiv(shader: GLESShader, pname: number): number; getShaderInfoLog(shader: GLESShader | null): string | null; - getShaderPrecisionFormat(shadertype: number, precisiontype: number): GLESShaderPrecisionFormat | null; + getShaderPrecisionFormat( + shadertype: number, + precisiontype: number + ): GLESShaderPrecisionFormat | null; getShaderSource(shader: GLESShader | null): string | null; getString(name: number): string; getTexParameterfv(target: number, pname: number): number; getTexParameteriv(target: number, pname: number): number; - getUniformfv(program: GLESProgram | null, location: GLESUniformLocation | null): number; - getUniformiv(program: GLESProgram | null, location: GLESUniformLocation | null): number; - getUniformLocation(program: GLESProgram | null, name: string): GLESUniformLocation | null; + getUniformfv( + program: GLESProgram | null, + location: GLESUniformLocation | null + ): number; + getUniformiv( + program: GLESProgram | null, + location: GLESUniformLocation | null + ): number; + getUniformLocation( + program: GLESProgram | null, + name: string + ): GLESUniformLocation | null; getVertexAttribfv(index: number, pname: number): number; getVertexAttribiv(index: number, pname: number): number; getVertexAttribPointerv(index: number, pname: number): number; @@ -127,17 +200,36 @@ declare interface GLESRenderingContext extends WebGL2RenderingContext { polygonOffset(factor: number, units: number): void; // readPixels(x: number, y: number, width: number, height: number, format: number, type: number, pixels: ArrayBufferView | null): void; releaseShaderCompiler(): void; - renderbufferStorage(target: number, internalformat: number, width: number, height: number): void; + renderbufferStorage( + target: number, + internalformat: number, + width: number, + height: number + ): void; sampleCoverage(value: number, invert: boolean): void; scissor(x: number, y: number, width: number, height: number): void; - shaderBinary(shader: GLESShader, binaryformat: number, bin: ArrayBuffer | ArrayBufferView): void; + shaderBinary( + shader: GLESShader, + binaryformat: number, + bin: ArrayBuffer | ArrayBufferView + ): void; shaderSource(shader: GLESShader | null, source: string): void; stencilFunc(func: number, ref: number, mask: number): void; - stencilFuncSeparate(face: number, func: number, ref: number, mask: number): void; + stencilFuncSeparate( + face: number, + func: number, + ref: number, + mask: number + ): void; stencilMask(mask: number): void; stencilMaskSeparate(face: number, mask: number): void; stencilOp(fail: number, zfail: number, zpass: number): void; - stencilOpSeparate(face: number, fail: number, zfail: number, zpass: number): void; + stencilOpSeparate( + face: number, + fail: number, + zfail: number, + zpass: number + ): void; // texImage2D(target: number, level: number, internalformat: number, width: number, height: number, border: number, format: number, type: number, pixels: ArrayBufferView | null): void; texParameterf(target: number, pname: number, param: number): void; texParameterfv(target: number, pname: number, params: Float32Array): void; @@ -152,17 +244,51 @@ declare interface GLESRenderingContext extends WebGL2RenderingContext { uniform2fv(location: GLESUniformLocation, v: Float32Array): void; uniform2i(location: GLESUniformLocation | null, x: number, y: number): void; uniform2iv(location: GLESUniformLocation, v: Int32Array): void; - uniform3f(location: GLESUniformLocation | null, x: number, y: number, z: number): void; + uniform3f( + location: GLESUniformLocation | null, + x: number, + y: number, + z: number + ): void; uniform3fv(location: GLESUniformLocation, v: Float32Array): void; - uniform3i(location: GLESUniformLocation | null, x: number, y: number, z: number): void; + uniform3i( + location: GLESUniformLocation | null, + x: number, + y: number, + z: number + ): void; uniform3iv(location: GLESUniformLocation, v: Int32Array): void; - uniform4f(location: GLESUniformLocation | null, x: number, y: number, z: number, w: number): void; + uniform4f( + location: GLESUniformLocation | null, + x: number, + y: number, + z: number, + w: number + ): void; uniform4fv(location: GLESUniformLocation, v: Float32Array): void; - uniform4i(location: GLESUniformLocation | null, x: number, y: number, z: number, w: number): void; + uniform4i( + location: GLESUniformLocation | null, + x: number, + y: number, + z: number, + w: number + ): void; uniform4iv(location: GLESUniformLocation, v: Int32Array): void; - uniformMatrix2fv(location: GLESUniformLocation, transpose: boolean, value: Float32Array): void; - uniformMatrix3fv(location: GLESUniformLocation, transpose: boolean, value: Float32Array): void; - uniformMatrix4fv(location: GLESUniformLocation, transpose: boolean, value: Float32Array): void; + uniformMatrix2fv( + location: GLESUniformLocation, + transpose: boolean, + value: Float32Array + ): void; + uniformMatrix3fv( + location: GLESUniformLocation, + transpose: boolean, + value: Float32Array + ): void; + uniformMatrix4fv( + location: GLESUniformLocation, + transpose: boolean, + value: Float32Array + ): void; useProgram(program: GLESProgram | null): void; validateProgram(program: GLESProgram | null): void; vertexAttrib1f(indx: number, x: number): void; @@ -171,9 +297,22 @@ declare interface GLESRenderingContext extends WebGL2RenderingContext { vertexAttrib2fv(indx: number, values: Float32Array): void; vertexAttrib3f(indx: number, x: number, y: number, z: number): void; vertexAttrib3fv(indx: number, values: Float32Array): void; - vertexAttrib4f(indx: number, x: number, y: number, z: number, w: number): void; + vertexAttrib4f( + indx: number, + x: number, + y: number, + z: number, + w: number + ): void; vertexAttrib4fv(indx: number, values: Float32Array): void; - vertexAttribPointer(indx: number, size: number, type: number, normalized: boolean, stride: number, offset: number): void; + vertexAttribPointer( + indx: number, + size: number, + type: number, + normalized: boolean, + stride: number, + offset: number + ): void; viewport(x: number, y: number, width: number, height: number): void; readonly DEPTH_BUFFER_BIT: number; diff --git a/src/glsl.d.ts b/src/glsl.d.ts index 4846ef1..4e93dbe 100644 --- a/src/glsl.d.ts +++ b/src/glsl.d.ts @@ -1,4 +1,4 @@ -declare module '*.glsl' { +declare module "*.glsl" { const value: string; export default value; } diff --git a/src/index.ts b/src/index.ts index 103e734..32ece0a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,28 +1,27 @@ - // Offline-Worker entry-point -export * from './offline/OfflineHelper'; +export * from "./offline/OfflineHelper"; // custom effects -export * from './three'; +export * from "./three"; // three.ts lib -export * from './three.ts/src'; +export * from "./three.ts/src"; // Web-AssemblyScript entry-point -export * from './wasc-worker'; +export * from "./wasc-worker"; // audio processing -export * from './weas'; +export * from "./weas"; // single modules -export * from './CComponent'; -export * from './CSettings'; -export * from './FPSta'; -export * from './LoadHelper'; -export * from './ReloadHelper'; -export * from './Smallog'; -export * from './Util'; -export * from './WarnHelper'; -export * from './WEICUE'; -export * from './WEWA'; -export * from './XRHelper'; +export * from "./CComponent"; +export * from "./CSettings"; +export * from "./FPSta"; +export * from "./LoadHelper"; +export * from "./ReloadHelper"; +export * from "./Smallog"; +export * from "./Util"; +export * from "./WarnHelper"; +export * from "./WEICUE"; +export * from "./WEWA"; +export * from "./XRHelper"; diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js index d2ac34a..4bc9ecb 100644 --- a/src/renamer/RenamerPlugin.js +++ b/src/renamer/RenamerPlugin.js @@ -1,40 +1,40 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -* -* @todo -* - export normal strings? -* -* @ignore -*/ - -const lib = require('./jsfuck.js'); - -const {RawSource} = require('webpack-sources'); - -const validate = require('schema-utils'); -const pluginName = 'RenamerPlugin'; + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @todo + * - export normal strings? + * + * @ignore + */ + +const lib = require("./jsfuck.js"); + +const { RawSource } = require("webpack-sources"); + +const validate = require("schema-utils"); +const pluginName = "RenamerPlugin"; /** -* schema for options object -* @see {RenamerPlugin} -*/ + * schema for options object + * @see {RenamerPlugin} + */ const offlineSchema = { - type: 'object', + type: "object", properties: { regex: { - type: 'object', + type: "object", }, }, }; /** -* Bruh -*/ + * Bruh + */ class RenamerPlugin { options = {}; @@ -42,22 +42,26 @@ class RenamerPlugin { nameMap = []; /** - * Intializes the plugin in the webpack build process - * @param {offlineSchema} options - plugin options - */ + * Intializes the plugin in the webpack build process + * @param {offlineSchema} options - plugin options + */ constructor(options = {}) { validate.validate(offlineSchema, options); this.options = options; } /** - * Get a random variable name, which does not exist in src and mappings - * @param {string} src - source code - * @return {string} random variable name - */ + * Get a random variable name, which does not exist in src and mappings + * @param {string} src - source code + * @return {string} random variable name + */ getRandomName(src) { - const gen = '$x' + Math.random().toString(20).substr(2, 2 + Math.random() * 4); - let exist = (src || '').indexOf(gen) >= 0; + const gen = + "$x" + + Math.random() + .toString(20) + .substr(2, 2 + Math.random() * 4); + let exist = (src || "").indexOf(gen) >= 0; this.nameMap.forEach((mping) => { if (mping.key === gen) exist = true; }); @@ -65,11 +69,11 @@ class RenamerPlugin { } /** - * Regex replace "match" function - * @param {string} source - source code - * @param {string} match - regex match - * @return {string} replaced string - */ + * Regex replace "match" function + * @param {string} source - source code + * @param {string} match - regex match + * @return {string} replaced string + */ replaceMatch(source, match) { let fnd = null; // check if this exact name is already mapped @@ -79,18 +83,18 @@ class RenamerPlugin { if (fnd) return fnd; // get & add a new random variable name fnd = this.getRandomName(source); - this.nameMap.push({key: match, val: fnd}); + this.nameMap.push({ key: match, val: fnd }); return fnd; } - /** * randomize array * @param {Array} array - array to randomize * @return {Array} randomized array */ shuffle(array) { - let currentIndex = array.length; let randomIndex; + let currentIndex = array.length; + let randomIndex; // While there remain elements to shuffle... while (currentIndex != 0) { // Pick a remaining element... @@ -98,7 +102,9 @@ class RenamerPlugin { currentIndex--; // And swap it with the current element. [array[currentIndex], array[randomIndex]] = [ - array[randomIndex], array[currentIndex]]; + array[randomIndex], + array[currentIndex], + ]; } return array; } @@ -110,21 +116,53 @@ class RenamerPlugin { */ shortenAccessors(pd) { // 杀 屠 大 门 安 天 - const candids = this.shuffle('職 識 辦 辯 色 特 持 谁 准 彩 就 是 空 虚 纸 张 图 片 末 未 已 己 土 士 干 千 人 入'.split(' ')); + const candids = this.shuffle( + "職 識 辦 辯 色 特 持 谁 准 彩 就 是 空 虚 纸 张 图 片 末 未 已 己 土 士 干 千 人 入".split( + " " + ) + ); let globalPre = candids[0]; let candI = 1; // @todo from settings - const blackList = ['.arguments', '.update', '.outputData', '.audioProps', '.inputData', '.levelSettings', - '.exports', '.build', '.forEach', '.buffer', - '.__getFloat64ArrayView', '.__getInt32Array', '.__getFloat32ArrayView']; + const blackList = [ + ".arguments", + ".update", + ".outputData", + ".audioProps", + ".inputData", + ".levelSettings", + ".exports", + ".build", + ".forEach", + ".buffer", + ".__getFloat64ArrayView", + ".__getInt32Array", + ".__getFloat32ArrayView", + ]; + + // used runtime string delimiter + const delim = "."; // count all accessor usages, which could gain an advantage by shortening const processed = {}; pd.match(/\.+[a-zA-Z0-9_]{6,}/g).forEach((element) => { const match = element; - if (match.indexOf('.') != 0) return; // only dot at start allowed - if (match.indexOf('..') == 0) return; // skip spread operator + if (match.indexOf(".") != 0) return; // only dot at start allowed + if (match.indexOf("..") == 0) return; // skip spread operator + if (blackList.includes(match)) return; + + if (processed[match]) processed[match]++; + else processed[match] = 1; + }); + + // queue to replace "" strings + // queue to replace '' strings + const strPatt = /('(?:\\.|[^'])*')|("(?:\\.|[^"])*")/gim; + pd.match(strPatt).forEach((element) => { + const match = element; + if (match.indexOf(".") >= 0) return; // no dots allowed + if (match.indexOf(delim) >= 0) return; // skip delim operator if (blackList.includes(match)) return; if (processed[match]) processed[match]++; @@ -134,10 +172,10 @@ class RenamerPlugin { // convert & calculate the actual 'char' savings const converted = Object.keys(processed).map((key) => { const val = processed[key]; - const saving = val * (key.length) - (key.length + 3); - return {k: key, v: val, w: saving}; + const saving = val * key.length - (key.length + 3); + return { k: key, v: val, w: saving }; }); - converted.sort((b, a) => (a.w > b.w) ? 1 : ((b.w > a.w) ? -1 : 0)); + converted.sort((b, a) => (a.w > b.w ? 1 : b.w > a.w ? -1 : 0)); // filter positive savings only const globalMap = {}; @@ -146,8 +184,8 @@ class RenamerPlugin { const filtered = []; let realI = 0; let canCount = 0; - const minSum = (globalPre.length + 8); // 'var =[];' - let sum = - minSum; + const minSum = globalPre.length + 8; // 'var =[];' + let sum = -minSum; for (let i = 0; i < converted.length; i++) { const element = converted[i]; @@ -159,49 +197,65 @@ class RenamerPlugin { sum -= minSum; } - const replacement = '[' + globalPre + '[' + realI + ']]'; + const replacement = "[" + globalPre + "[" + realI + "]]"; const newLen = element.v * replacement.length; const newWeight = element.w - newLen; // if the advantage exceeds the own weight, process it if (newWeight > element.k.length) { globalMap[globalPre][realI++] = element.k.substring(1); canCount++; - filtered.push({k: element.k, v: element.v, w: newWeight, r: replacement}); + filtered.push({ + k: element.k, + v: element.v, + w: newWeight, + r: replacement, + }); sum += newWeight; } } // !! important to replace the longest words first, so we dont have partial replacements !! - filtered.sort((b, a) => (a.k.length > b.k.length) ? 1 : ((b.k.length > a.k.length) ? -1 : 0)); + filtered.sort((b, a) => + a.k.length > b.k.length ? 1 : b.k.length > a.k.length ? -1 : 0 + ); // console.log(JSON.stringify(filtered, null, '\t')); - console.log('Potentially saving: ' + sum + ' chars.'); + console.log("Potentially saving: " + sum + " chars."); const oldPd = pd; // replace all occurences for (let index = 0; index < filtered.length; index++) { const element = filtered[index]; - console.log('Replacing ' + element.k + ' => ' + element.r + ' (' + element.v + ' usages)'); + console.log( + "Replacing " + + element.k + + " => " + + element.r + + " (" + + element.v + + " usages)" + ); let skipped = 0; let newPd = pd; // check & queue every potential replacement one-by-one const operations = []; - const rgx = new RegExp('\\' + element.k, 'g'); + const rgx = new RegExp("\\" + element.k, "g"); let rm; while ((rm = rgx.exec(newPd)) != null) { const rIdx = rm.index; // match is invalid if it is followed by a alphanumeric char or _ const followChar = newPd[rgx.lastIndex]; - let isInvalid = (followChar.match(/[a-zA-Z0-9_]/) != null); + let isInvalid = followChar.match(/[a-zA-Z0-9_]/) != null; // check if replacement is in "" string // check if replacement is in '' string // check if replacement is in `` string - const patt = /'((?:\\.|[^'])*)'|"((?:\\.|[^"])*)"|`((?:\\.|[^`])*)`/igm; + const patt = + /'((?:\\.|[^'])*)'|"((?:\\.|[^"])*)"|`((?:\\.|[^`])*)`/gim; let match; while ((match = patt.exec(newPd)) != null && !isInvalid) { // console.log(match.index + ' ' + patt.lastIndex); @@ -213,7 +267,11 @@ class RenamerPlugin { } if (!isInvalid) { - operations.push({start: rIdx, end: rgx.lastIndex, repl: element.r}); + operations.push({ + start: rIdx, + end: rgx.lastIndex, + repl: element.r, + }); } else { skipped++; } @@ -230,7 +288,7 @@ class RenamerPlugin { newPd = newPd.substring(0, from) + re + newPd.substring(to); // calculate new offset for following pending operations - const off = (to - from) - re.length; + const off = to - from - re.length; for (let idx = index + 1; idx < operations.length; idx++) { const op = operations[idx]; if (op.start > to - off) op.start -= off; @@ -241,31 +299,30 @@ class RenamerPlugin { // sanity check // @todo bruh if (newPd.match(/\]\][a-zA-Z0-9_]{1,}/)) { - console.log('Error double bracket, missing delimiter? '); + console.log("Error double bracket, missing delimiter? "); console.log(element); } else { - if (skipped > 0) console.log(`Skipped ${skipped} invalid replacements.`); + if (skipped > 0) + console.log(`Skipped ${skipped} invalid replacements.`); pd = newPd; } } - // prepend all global Maps - const delim = '.'; - const splFunc = '大'; + const splFunc = "大"; const rndOff = Math.round(2 + Math.random() * 14); // eslint-disable-next-line require-jsdoc function caesarCipher(s) { - let r = ''; + let r = ""; for (let i = 0; i < s.length; i++) { - r += String.fromCharCode((s[i].charCodeAt()) + rndOff); + r += String.fromCharCode(s[i].charCodeAt() + rndOff); } return r; } function mayEncode(str) { - if(Math.random() > 0.5) { + if (Math.random() > 0.5) { return lib.JSFuck.encode(str); } return `'${str}'`; @@ -275,15 +332,14 @@ class RenamerPlugin { const keys = Object.keys(globalMap); if (keys.length > 4) { const fk1 = lib.JSFuck.encode(rndOff.toString()); - const fk2 = mayEncode('split'); + const fk2 = mayEncode("split"); const fk3 = lib.JSFuck.encode(delim); - const fk4 = mayEncode('length'); - const fk5 = mayEncode('harCode'); - const fk6 = mayEncode('atob'); - const fk7 = mayEncode('String'); - const tmp1 = '`fromC${ë}`'; - const tmp2 = '`c${ë}At`'; - const func = `é=>{var è='',ë=${fk5},ė=window;for(var ê=0,é=ė[${fk6}](é);ê<é[${fk4}];è+=ė[${fk7}][${tmp1}]((é[ê++][${tmp2}]())-(${fk1}))){}return è[${fk2}](${fk3})}`; + const fk4 = mayEncode("length"); + const fk6 = lib.JSFuck.encode("atob"); + const fk7 = mayEncode("String"); + const tmp1 = "`fromC${ë}`"; + const tmp2 = "`c${ë}At`"; + const func = `é=>{var è=window,ë='harCode',ě=${fk4},ė=è,è='';for(var ê=0,é=ė[${fk6}](é);ê<é[ě];è+=ė[${fk7}][${tmp1}]((é[ê++][${tmp2}]())-(${fk1}))){}return è[${fk2}](${fk3})}`; newStrict += `var ${splFunc}=${func},`; } @@ -291,36 +347,38 @@ class RenamerPlugin { let val = globalMap[k]; if (val.length > 4) { if (keys.length > 4) { - val = `${splFunc}('${btoa(caesarCipher(val.join(delim)))}')`; + val = `${splFunc}('${btoa( + caesarCipher(val.join(delim)) + )}')`; } else { val = `"${val.join(delim)}".split('${delim}')`; } } else { val = JSON.stringify(val); } - if (newStrict.endsWith(')' || newStrict.endsWith('}'))) { - newStrict += ','; + if (newStrict.endsWith(")" || newStrict.endsWith("}"))) { + newStrict += ","; } - if (newStrict.endsWith(';')) { - newStrict += 'var '; + if (newStrict.endsWith(";")) { + newStrict += "var "; } newStrict += `${k}=${val}`; }); console.log(newStrict); - pd = newStrict + ';' + pd; + pd = newStrict + ";" + pd; const lengDiff = oldPd.length - pd.length; const expDiff = lengDiff - sum; - console.log('Replacement complete!'); - let m = 'Actually saved: ' + lengDiff + ' chars. '; - if (expDiff > 0) m += ' (' + expDiff + ' more than expected)'; - if (expDiff < 0) m += ' (' + Math.abs(expDiff) + ' less than expected)'; + console.log("Replacement complete!"); + let m = "Actually saved: " + lengDiff + " chars. "; + if (expDiff > 0) m += " (" + expDiff + " more than expected)"; + if (expDiff < 0) m += " (" + Math.abs(expDiff) + " less than expected)"; console.log(m); if (lengDiff < 0) { - console.log('Did not save any chars! rolling back...'); + console.log("Did not save any chars! rolling back..."); pd = oldPd; } @@ -333,12 +391,14 @@ class RenamerPlugin { * @return {string} processed source */ processString(source) { - if (typeof source !== 'string') { - console.error('Source no string: ', source); + if (typeof source !== "string") { + console.error("Source no string: ", source); return source; } - const pd = source.replace(this.options.regex, (match) => this.replaceMatch(source, match)); // .replaceAll('const ', 'var '); + const pd = source.replace(this.options.regex, (match) => + this.replaceMatch(source, match) + ); // .replaceAll('const ', 'var '); const sa = this.shortenAccessors(pd); @@ -354,36 +414,42 @@ class RenamerPlugin { */ processSource(compilation, name, child) { if (child._valueIsBuffer) { - console.log('Value is Buffer!'); + console.log("Value is Buffer!"); return; } const source = child.source._value; const processed = this.processString(source); // if anything changed, update the processed asset - if (typeof processed === 'string' && source != processed) { + if (typeof processed === "string" && source != processed) { compilation.updateAsset(name, new RawSource(processed)); // calculate saved memory - const savedChars = (source.length - processed.length); - console.info('[' + pluginName + '] Saved: ' + savedChars + ' chars'); + const savedChars = source.length - processed.length; + console.info( + "[" + pluginName + "] Saved: " + savedChars + " chars" + ); } } /** - * Hook into the compilation process, - * Replace regex matches with random strings - * @param {Webpack.compiler} compiler object from webpack - * @return {void} - */ + * Hook into the compilation process, + * Replace regex matches with random strings + * @param {Webpack.compiler} compiler object from webpack + * @return {void} + */ apply(compiler) { compiler.hooks.emit.tap(pluginName, (compilation) => { try { - console.info('[' + pluginName + '] Using Regex: ' + this.options.regex); + console.info( + "[" + pluginName + "] Using Regex: " + this.options.regex + ); // process all compiled .js files for (const assetFile in compilation.assets) { - if (!assetFile || !assetFile.endsWith('.js')) continue; - console.info('[' + pluginName + '] Processing: ' + assetFile); + if (!assetFile || !assetFile.endsWith(".js")) continue; + console.info( + "[" + pluginName + "] Processing: " + assetFile + ); // get the processed asset object / source const asset = compilation.getAsset(assetFile); @@ -398,14 +464,12 @@ class RenamerPlugin { } // finish up - console.info('[' + pluginName + '] Replaced: ', this.nameMap); + console.info("[" + pluginName + "] Replaced: ", this.nameMap); } catch (error) { - console.info('[' + pluginName + '] Replace error: ', error); + console.info("[" + pluginName + "] Replace error: ", error); } }); } } module.exports = RenamerPlugin; - - diff --git a/src/three.ts b/src/three.ts index 5468b4c..22d43b6 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 5468b4cade2930ffdaefc68a4f4e2ac366b980e7 +Subproject commit 22d43b618ae19a0868eaac11175f3bf54bb13edc diff --git a/src/three/pass/ShaderPass.ts b/src/three/pass/ShaderPass.ts index fd20926..c2cccf7 100644 --- a/src/three/pass/ShaderPass.ts +++ b/src/three/pass/ShaderPass.ts @@ -30,68 +30,65 @@ export class ShaderPass implements BasePass { iRes: Vector2; /** - * Make Pass - * default Material will enable transparency! - * @param {BaseShader|ShaderMaterial} shader Create From - * @param {string} textureID Input Uniform Texture name - */ - constructor(shader: BaseShader | ShaderMaterial, textureID = "tDiffuse") { + * Make Pass + * default Material will enable transparency! + * @param {BaseShader} shader Create From + * @param {string} textureID Input Uniform Texture name + */ + constructor(shader: BaseShader, textureID = "tDiffuse") { this.textureID = textureID; - if (shader instanceof ShaderMaterial) { - this.name = "ShaderMaterial"; - this.uniforms = shader.uniforms; - this.material = shader; - } else if (shader) { - this.name = shader.shaderID; - this.uniforms = UniformsUtils.clone(shader.uniforms); - this.material = new ShaderMaterial(); - this.material.defines = Object.assign({}, shader.defines); - this.material.uniforms = this.uniforms; - this.material.vertexShader = shader.vertexShader; - this.material.fragmentShader = shader.fragmentShader; - } + this.name = shader.shaderID; + + this.uniforms = UniformsUtils.clone(shader.uniforms); + + this.material = new ShaderMaterial(); + this.material.defines = Object.assign({}, shader.defines); + this.material.uniforms = this.uniforms; + this.material.vertexShader = shader.vertexShader; + this.material.fragmentShader = shader.fragmentShader; this.material.transparent = true; + this.fsQuad = new FullScreenHelper(this.material); } /** - * precompile shader - * @param {WebGLRenderer} renderer renderer - * @returns {void} - */ + * precompile shader + * @param {WebGLRenderer} renderer renderer + * @returns {void} + */ prepare(renderer: WebGLRenderer): void { this.fsQuad.prepare(renderer); } /** - * Destroy Pass - * @public - * @returns {void} - */ + * Destroy Pass + * @public + * @returns {void} + */ dispose() { this.fsQuad.dispose(); } /** - * Canvas size update - * @param {number} width X - * @param {number} height Y - * @returns {void} - */ + * Canvas size update + * @param {number} width X + * @param {number} height Y + * @returns {void} + */ setSize(width: number, height: number) { this.iRes = new Vector2(width, height); } /** - * Render frame with chaining-support - * @param {WebGLRenderer} renderer renderer - * @param {WebGLRenderTarget} writeBuffer wB - * @param {WebGLRenderTarget} readBuffer rB - * @param {boolean} maskActive mA - * @param {boolean} renderToScreen render to canvas OR buffer - * @returns {void} - */ + * Render frame with chaining-support + * @param {WebGLRenderer} renderer renderer + * @param {WebGLRenderTarget} writeBuffer wB + * @param {WebGLRenderTarget} readBuffer rB + * @param {boolean} maskActive mA + * @param {boolean} renderToScreen render to canvas OR buffer + * @returns {void} + */ render( renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, diff --git a/src/three/shader/fragment/FractalMirror.glsl b/src/three/shader/fragment/FractalMirror.glsl index 1217531..c52abdb 100644 --- a/src/three/shader/fragment/FractalMirror.glsl +++ b/src/three/shader/fragment/FractalMirror.glsl @@ -11,14 +11,12 @@ void main() { vec2 center = vec2(0.5, 0.5); float zoom = iResolution.x / iResolution.y; vec2 uv = center - vUv; - + if(zoom > 1.0) { uv.y /= zoom; - } - else { + } else { uv.x *= zoom; } - float KA = PI / numSides; float angle = abs(mod(atan(uv.y, uv.x), 2.0 * KA) - KA); @@ -30,8 +28,7 @@ void main() { vec2 transformed = length(uv) * vec2(sin(angle), cos(angle)); if(!invert) { transformed += center; - } - else { + } else { if(transformed.x < 0.0) { transformed.x += 1.0; } diff --git a/src/wasc-worker b/src/wasc-worker index 176de5f..a041d6e 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 176de5f1fbbdb2fa4963966196ae8cae5a6357c1 +Subproject commit a041d6ef80a19fe61f07ff1455a0c5dacae69286 diff --git a/src/weas/Bea.ts b/src/weas/Bea.ts index ab6d59d..129956a 100644 --- a/src/weas/Bea.ts +++ b/src/weas/Bea.ts @@ -1,38 +1,38 @@ /* eslint-disable guard-for-in */ /* -* BeatDetektor.js -* -* BeatDetektor - CubicFX Visualizer Beat Detection & Analysis Algorithm -* Javascript port by Charles J. Cliffe and Corban Brook -* -* Copyright (c) 2009 Charles J. Cliffe. -* -* BeatDetektor is distributed under the terms of the MIT License. -* http://opensource.org/licenses/MIT -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -* IN THE SOFTWARE. -*/ + * BeatDetektor.js + * + * BeatDetektor - CubicFX Visualizer Beat Detection & Analysis Algorithm + * Javascript port by Charles J. Cliffe and Corban Brook + * + * Copyright (c) 2009 Charles J. Cliffe. + * + * BeatDetektor is distributed under the terms of the MIT License. + * http://opensource.org/licenses/MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ /** -* @public -*/ + * @public + */ export class Bea_ts { private bd_low: BeatsDetektor = new BeatsDetektor(55, 109); private bd_med: BeatsDetektor = new BeatsDetektor(85, 169); @@ -44,13 +44,13 @@ export class Bea_ts { private bds: BeatsDetektor[] = []; private bdi = 0; - private prev_bpm: number = -1; - private prev_win: number = -1; + private prev_bpm = -1; + private prev_win = -1; /** - * Construct BeatDetektor array - * @param {boolean} autoReset stfu - */ + * Construct BeatDetektor array + * @param {boolean} autoReset stfu + */ constructor(autoReset = true) { this.bds.push(this.bd_low, this.bd_med, this.bd_high, this.bd_ext); @@ -68,12 +68,12 @@ export class Bea_ts { } /** - * Bpm-Process current data - * @param {number} time timestamp as float - * @param {Array} data fftData - * @return {Array} - * @public - */ + * Bpm-Process current data + * @param {number} time timestamp as float + * @param {Array} data fftData + * @return {Array} + * @public + */ public process(time, data) { const groupings = true; const chooseAvg = false; @@ -82,7 +82,7 @@ export class Bea_ts { // weigh previous result in if (this.prev_bpm > 0) { - results.push({value: this.prev_bpm, weight: this.prev_win}); + results.push({ value: this.prev_bpm, weight: this.prev_win }); } this.bds.forEach((bd) => { @@ -91,13 +91,13 @@ export class Bea_ts { const tmp = []; let maxW = 0; bd.bpm_contest.forEach((val, key) => { - tmp.push({value: key / 10, weight: val}); + tmp.push({ value: key / 10, weight: val }); if (val > maxW) maxW = val; }); - const srt = tmp.sort((a, b) => b.weight- a.weight); + const srt = tmp.sort((a, b) => b.weight - a.weight); for (let i = 0; i < Math.min(srt.length, 10); i++) { const obj = srt[i]; - results.push({value: obj.value, weight: obj.weight /* / maxW */}); + results.push({ value: obj.value, weight: obj.weight /* / maxW */ }); } } }); @@ -122,13 +122,17 @@ export class Bea_ts { const mn = Math.min(result.value, comp.value); const ismn = comp.value == mn; let mult = mx / mn; - if (mult > 2.5 && mult < 3.5) mult /= 3; // max is ~ 3x multiple of min - else if (mult > 1.5 && mult < 2.50) mult /= 2; // max is ~ 2x multiple of min + if (mult > 2.5 && mult < 3.5) mult /= 3; + // max is ~ 3x multiple of min + else if (mult > 1.5 && mult < 2.5) mult /= 2; // max is ~ 2x multiple of min // do weighing calculation let weight = 0; - if (mult > 0.96 && mult < 1.04) weight = (ismn ? 1.0 : 1.0) - Math.abs(mult - 1.00); // is 1/1 beat - if (mult > 0.64 && mult < 0.68) weight = (ismn ? 0.8 : 0.5) - Math.abs(mult - 0.66); // is 2/3 beat - if (mult > 0.72 && mult < 0.78) weight = (ismn ? 0.3 : 0.4) - Math.abs(mult - 0.75); // is 3/4 beat + if (mult > 0.96 && mult < 1.04) + weight = (ismn ? 1.0 : 1.0) - Math.abs(mult - 1.0); // is 1/1 beat + if (mult > 0.64 && mult < 0.68) + weight = (ismn ? 0.8 : 0.5) - Math.abs(mult - 0.66); // is 2/3 beat + if (mult > 0.72 && mult < 0.78) + weight = (ismn ? 0.3 : 0.4) - Math.abs(mult - 0.75); // is 3/4 beat // calculate sum resWeight += weight * comp.weight; } @@ -136,7 +140,7 @@ export class Bea_ts { let value = result.value; if (value < 90) value *= 2; if (value > 180) value /= 2; - candidates.push({value, weight: (result.weight * 2 + resWeight) / 3}); + candidates.push({ value, weight: (result.weight * 2 + resWeight) / 3 }); } // group by rounded numbers x,0 | x,5 | x+1,0 @@ -157,8 +161,8 @@ export class Bea_ts { grps[val] = (grps[val] + this.winRewards[v2]) / 2; } }); - candidates = grps.map((v, i) =>{ - return {value: i / 10, weight: v}; + candidates = grps.map((v, i) => { + return { value: i / 10, weight: v }; }); } @@ -167,9 +171,9 @@ export class Bea_ts { // get average if (chooseAvg) { - const half = (candidates.length / 2); + const half = candidates.length / 2; let avg = 0; - candidates.forEach((c, i) => avg += i < half ? c.value : 0); + candidates.forEach((c, i) => (avg += i < half ? c.value : 0)); avg /= half; // choose closest to average let choiceI = -1; @@ -255,7 +259,6 @@ export class Bea_ts { } } - /** BeatDetektor class @@ -338,16 +341,28 @@ class BeatsDetektor { BD_MINIMUM_CONTRIBUTIONS: 8, // At least x ranges must agree to process a result BD_FINISH_LINE: 60.0, // Contest values wil be normalized to this finish line // this is the 'funnel' that pulls ranges in / out of alignment based on trigger detection - BD_REWARD_TOLERANCES: [0.001, 0.005, 0.01, 0.02, 0.04, 0.08, 0.10, 0.15, 0.30], // .1%, .5%, 1%, 2%, 4%, 8%, 10%, 15% - BD_REWARD_MULTIPLIERS: [20.0, 10.0, 8.0, 1.0, 1.0/2.0, 1.0/4.0, 1.0/8.0, 1/16.0, 1/32.0], + BD_REWARD_TOLERANCES: [ + 0.001, 0.005, 0.01, 0.02, 0.04, 0.08, 0.1, 0.15, 0.3, + ], // .1%, .5%, 1%, 2%, 4%, 8%, 10%, 15% + BD_REWARD_MULTIPLIERS: [ + 20.0, + 10.0, + 8.0, + 1.0, + 1.0 / 2.0, + 1.0 / 4.0, + 1.0 / 8.0, + 1 / 16.0, + 1 / 32.0, + ], }; BPM_MIN: any; BPM_MAX: any; - beat_counter: number = 0; - half_counter: number = 0; - quarter_counter: number = 0; + beat_counter = 0; + half_counter = 0; + quarter_counter = 0; a_freq_range: Float64Array; ma_freq_range: Float64Array; @@ -361,42 +376,42 @@ class BeatsDetektor { detection_quality: Float64Array; detection: Array; - ma_quality_avg: number = 0; - ma_quality_total: number = 0; + ma_quality_avg = 0; + ma_quality_total = 0; bpm_contest: number[]; bpm_contest_lo: number[]; - quality_total: number = 0; - quality_avg: number = 0; + quality_total = 0; + quality_avg = 0; - current_bpm: number = 0; - current_bpm_lo: number = 0; - winning_bpm: number = 0; - win_val: number = 0; - winning_bpm_lo: number = 0; - win_val_lo: number = 0; + current_bpm = 0; + current_bpm_lo = 0; + winning_bpm = 0; + win_val = 0; + winning_bpm_lo = 0; + win_val_lo = 0; - win_bpm_int: number = 0; - win_bpm_int_lo: number = 0; + win_bpm_int = 0; + win_bpm_int_lo = 0; - bpm_predict: number = 0; - is_erratic: boolean = false; + bpm_predict = 0; + is_erratic = false; - bpm_offset: number = 0; + bpm_offset = 0; - last_timer: number = 0; - last_update: number = 0; - bpm_timer: number = 0; - maa_quality_avg: number = 0; + last_timer = 0; + last_update = 0; + bpm_timer = 0; + maa_quality_avg = 0; /** - * Create new instance of BeatsDetektor. See above for details - * @param {number} bpm_minimum estimation lower bound - * @param {number} bpm_maximum estimation upper bound - * @param {Object} alt_config alternative config (optional) - * @public - */ + * Create new instance of BeatsDetektor. See above for details + * @param {number} bpm_minimum estimation lower bound + * @param {number} bpm_maximum estimation upper bound + * @param {Object} alt_config alternative config (optional) + * @public + */ constructor(bpm_minimum = 85, bpm_maximum = 169, alt_config?) { if (alt_config) this.config = alt_config; @@ -426,14 +441,16 @@ class BeatsDetektor { this.reset(); if (console) { - console.log('BeatsDetektor('+this.BPM_MIN+','+this.BPM_MAX+') created.'); + console.log( + "BeatsDetektor(" + this.BPM_MIN + "," + this.BPM_MAX + ") created." + ); } } /** - * Null everthing - * @public - */ + * Null everthing + * @public + */ public reset() { // var bpm_avg = 60.0/((this.BPM_MIN+this.BPM_MAX)/2.0); @@ -443,8 +460,10 @@ class BeatsDetektor { this.maa_freq_range[i] = 0.0; this.last_detection[i] = 0.0; - this.ma_bpm_range[i] = - this.maa_bpm_range[i] = 60.0/this.BPM_MIN + ((60.0/this.BPM_MAX-60.0/this.BPM_MIN) * (i/this.config.BD_DETECTION_RANGES)); + this.ma_bpm_range[i] = this.maa_bpm_range[i] = + 60.0 / this.BPM_MIN + + (60.0 / this.BPM_MAX - 60.0 / this.BPM_MIN) * + (i / this.config.BD_DETECTION_RANGES); this.detection_quality[i] = 0.0; this.detection[i] = false; @@ -484,12 +503,12 @@ class BeatsDetektor { } /** - * Process BPM for fft Data Frame - * @param {number} timer_seconds - * @param {Array} fft_data - * @return {number} win_bpm_int - * @public - */ + * Process BPM for fft Data Frame + * @param {number} timer_seconds + * @param {Array} fft_data + * @return {number} win_bpm_int + * @public + */ public process(timer_seconds, fft_data): number { // first call if (!this.last_timer) { @@ -517,18 +536,17 @@ class BeatsDetektor { let x; let v; - const bpm_floor = 60.0/this.BPM_MAX; - const bpm_ceil = 60.0/this.BPM_MIN; + const bpm_floor = 60.0 / this.BPM_MAX; + const bpm_ceil = 60.0 / this.BPM_MIN; - const range_step = (fft_data.length / this.config.BD_DETECTION_RANGES); + const range_step = fft_data.length / this.config.BD_DETECTION_RANGES; let range = 0; - - for (x=0; x= this.maa_freq_range[range]); + const det = + this.ma_freq_range[range] * this.config.BD_DETECTION_FACTOR >= + this.maa_freq_range[range]; // compute bpm clamps for comparison to gap lengths // clamp detection averages to input ranges - if (this.ma_bpm_range[range] > bpm_ceil) this.ma_bpm_range[range] = bpm_ceil; - if (this.ma_bpm_range[range] < bpm_floor) this.ma_bpm_range[range] = bpm_floor; - if (this.maa_bpm_range[range] > bpm_ceil) this.maa_bpm_range[range] = bpm_ceil; - if (this.maa_bpm_range[range] < bpm_floor) this.maa_bpm_range[range] = bpm_floor; + if (this.ma_bpm_range[range] > bpm_ceil) + this.ma_bpm_range[range] = bpm_ceil; + if (this.ma_bpm_range[range] < bpm_floor) + this.ma_bpm_range[range] = bpm_floor; + if (this.maa_bpm_range[range] > bpm_ceil) + this.maa_bpm_range[range] = bpm_ceil; + if (this.maa_bpm_range[range] < bpm_floor) + this.maa_bpm_range[range] = bpm_floor; let rewarded = false; // new detection since last, test it's quality if (!this.detection[range] && det) { // calculate length of gap (since start of last trigger) - let trigger_gap = timestamp-this.last_detection[range]; + let trigger_gap = timestamp - this.last_detection[range]; // trigger falls within acceptable range, - if (trigger_gap < bpm_ceil && trigger_gap > (bpm_floor)) { + if (trigger_gap < bpm_ceil && trigger_gap > bpm_floor) { // compute gap and award quality // use our tolerances as a funnel to edge detection towards the most likely value for (i = 0; i < this.config.BD_REWARD_TOLERANCES.length; i++) { - if (Math.abs(this.ma_bpm_range[range]-trigger_gap) < this.ma_bpm_range[range]*this.config.BD_REWARD_TOLERANCES[i]) { - this.detection_quality[range] += this.config.BD_QUALITY_REWARD * this.config.BD_REWARD_MULTIPLIERS[i]; + if ( + Math.abs(this.ma_bpm_range[range] - trigger_gap) < + this.ma_bpm_range[range] * this.config.BD_REWARD_TOLERANCES[i] + ) { + this.detection_quality[range] += + this.config.BD_QUALITY_REWARD * + this.config.BD_REWARD_MULTIPLIERS[i]; rewarded = true; } } @@ -583,16 +618,20 @@ class BeatsDetektor { // test for 1/2 beat trigger_gap /= 2.0; - if (trigger_gap < bpm_ceil && trigger_gap > (bpm_floor)) { + if (trigger_gap < bpm_ceil && trigger_gap > bpm_floor) { for (i = 0; i < this.config.BD_REWARD_TOLERANCES.length; i++) { - if (Math.abs(this.ma_bpm_range[range]-trigger_gap) < this.ma_bpm_range[range]*this.config.BD_REWARD_TOLERANCES[i]) { - this.detection_quality[range] += this.config.BD_QUALITY_REWARD * this.config.BD_REWARD_MULTIPLIERS[i]; + if ( + Math.abs(this.ma_bpm_range[range] - trigger_gap) < + this.ma_bpm_range[range] * this.config.BD_REWARD_TOLERANCES[i] + ) { + this.detection_quality[range] += + this.config.BD_QUALITY_REWARD * + this.config.BD_REWARD_MULTIPLIERS[i]; rewarded = true; } } } - // decrement quality if no 1/2 beat reward if (!rewarded) { trigger_gap *= 2.0; @@ -601,34 +640,62 @@ class BeatsDetektor { } if (rewarded) { - let qmp = (this.detection_quality[range]/this.quality_avg)*this.config.BD_QUALITY_STEP; + let qmp = + (this.detection_quality[range] / this.quality_avg) * + this.config.BD_QUALITY_STEP; if (qmp > 1.0) { qmp = 1.0; } - this.ma_bpm_range[range] -= (this.ma_bpm_range[range]-trigger_gap) * qmp; - this.maa_bpm_range[range] -= (this.maa_bpm_range[range]-this.ma_bpm_range[range]) * qmp; + this.ma_bpm_range[range] -= + (this.ma_bpm_range[range] - trigger_gap) * qmp; + this.maa_bpm_range[range] -= + (this.maa_bpm_range[range] - this.ma_bpm_range[range]) * qmp; } else if (trigger_gap >= bpm_floor && trigger_gap <= bpm_ceil) { - if (this.detection_quality[range] < this.quality_avg*this.config.BD_QUALITY_TOLERANCE && this.current_bpm) { - this.ma_bpm_range[range] -= (this.ma_bpm_range[range]-trigger_gap) * this.config.BD_QUALITY_STEP; - this.maa_bpm_range[range] -= (this.maa_bpm_range[range]-this.ma_bpm_range[range]) * this.config.BD_QUALITY_STEP; + if ( + this.detection_quality[range] < + this.quality_avg * this.config.BD_QUALITY_TOLERANCE && + this.current_bpm + ) { + this.ma_bpm_range[range] -= + (this.ma_bpm_range[range] - trigger_gap) * + this.config.BD_QUALITY_STEP; + this.maa_bpm_range[range] -= + (this.maa_bpm_range[range] - this.ma_bpm_range[range]) * + this.config.BD_QUALITY_STEP; } this.detection_quality[range] -= this.config.BD_QUALITY_STEP; } else if (trigger_gap >= bpm_ceil) { - if ((this.detection_quality[range] < this.quality_avg*this.config.BD_QUALITY_TOLERANCE) && this.current_bpm) { - this.ma_bpm_range[range] -= (this.ma_bpm_range[range]-this.current_bpm) * 0.5; - this.maa_bpm_range[range] -= (this.maa_bpm_range[range]-this.ma_bpm_range[range]) * 0.5; + if ( + this.detection_quality[range] < + this.quality_avg * this.config.BD_QUALITY_TOLERANCE && + this.current_bpm + ) { + this.ma_bpm_range[range] -= + (this.ma_bpm_range[range] - this.current_bpm) * 0.5; + this.maa_bpm_range[range] -= + (this.maa_bpm_range[range] - this.ma_bpm_range[range]) * 0.5; } - this.detection_quality[range]-= this.config.BD_QUALITY_STEP; + this.detection_quality[range] -= this.config.BD_QUALITY_STEP; } } - if ((!rewarded && timestamp-this.last_detection[range] > bpm_ceil) || (det && Math.abs(this.ma_bpm_range[range]-this.current_bpm) > this.bpm_offset)) { - this.detection_quality[range] -= this.detection_quality[range]*this.config.BD_QUALITY_STEP*this.config.BD_QUALITY_DECAY*this.last_update; + if ( + (!rewarded && timestamp - this.last_detection[range] > bpm_ceil) || + (det && + Math.abs(this.ma_bpm_range[range] - this.current_bpm) > + this.bpm_offset) + ) { + this.detection_quality[range] -= + this.detection_quality[range] * + this.config.BD_QUALITY_STEP * + this.config.BD_QUALITY_DECAY * + this.last_update; } // quality bottomed out, set to 0 - if (this.detection_quality[range] < 0.001) this.detection_quality[range]=0.001; + if (this.detection_quality[range] < 0.001) + this.detection_quality[range] = 0.001; this.detection[range] = det; @@ -643,24 +710,31 @@ class BeatsDetektor { // number of bpm ranges that contributed to this test let bpm_contributions = 0; - // accumulate quality weight total - for (let x=0; x= this.ma_quality_avg) { - if (this.ma_bpm_range[x] < bpm_ceil && this.ma_bpm_range[x] > bpm_floor) { + if ( + this.detection_quality[x] * this.config.BD_QUALITY_TOLERANCE >= + this.ma_quality_avg + ) { + if ( + this.ma_bpm_range[x] < bpm_ceil && + this.ma_bpm_range[x] > bpm_floor + ) { // eslint-disable-next-line no-unused-vars bpm_total += this.maa_bpm_range[x]; - let draft_float = Math.round((60.0/this.maa_bpm_range[x])*1000.0); - - draft_float = (Math.abs(Math.ceil(draft_float)-(60.0/this.current_bpm)*1000.0)<(Math.abs(Math.floor(draft_float)-(60.0/this.current_bpm)*1000.0)))?Math.ceil(draft_float/10.0):Math.floor(draft_float/10.0); - const draft_int = parseInt(draft_float/10.0 as any); + let draft_float = Math.round( + (60.0 / this.maa_bpm_range[x]) * 1000.0 + ); + + draft_float = + Math.abs( + Math.ceil(draft_float) - (60.0 / this.current_bpm) * 1000.0 + ) < + Math.abs( + Math.floor(draft_float) - (60.0 / this.current_bpm) * 1000.0 + ) + ? Math.ceil(draft_float / 10.0) + : Math.floor(draft_float / 10.0); + const draft_int = parseInt((draft_float / 10.0) as any); // if (draft_int) console.log(draft_int); if (isNaN(draft[draft_int])) draft[draft_int] = 0; - draft[draft_int]+=this.detection_quality[x]/this.quality_avg; + draft[draft_int] += this.detection_quality[x] / this.quality_avg; bpm_contributions++; if (offset_test_bpm == 0.0) offset_test_bpm = this.maa_bpm_range[x]; else { - avg_bpm_offset += Math.abs(offset_test_bpm-this.maa_bpm_range[x]); + avg_bpm_offset += Math.abs( + offset_test_bpm - this.maa_bpm_range[x] + ); } } } @@ -699,9 +791,10 @@ class BeatsDetektor { } // if we have one or more contributions that pass criteria then attempt to display a guess - const has_prediction = bpm_contributions>=this.config.BD_MINIMUM_CONTRIBUTIONS; + const has_prediction = + bpm_contributions >= this.config.BD_MINIMUM_CONTRIBUTIONS; - let draft_winner=0; + let draft_winner = 0; let win_val = 0; if (has_prediction) { @@ -712,7 +805,7 @@ class BeatsDetektor { } } - this.bpm_predict = 60.0/(draft_winner/10.0); + this.bpm_predict = 60.0 / (draft_winner / 10.0); avg_bpm_offset /= bpm_contributions; this.bpm_offset = avg_bpm_offset; @@ -722,71 +815,88 @@ class BeatsDetektor { } } - if (this.current_bpm && this.bpm_predict) this.current_bpm -= (this.current_bpm-this.bpm_predict)*this.last_update; + if (this.current_bpm && this.bpm_predict) + this.current_bpm -= + (this.current_bpm - this.bpm_predict) * this.last_update; // hold a contest for bpm to find the current mode - let contest_max=0; + let contest_max = 0; for (const contest_i in this.bpm_contest) { - if (contest_max < this.bpm_contest[contest_i]) contest_max = this.bpm_contest[contest_i]; - if (this.bpm_contest[contest_i] > this.config.BD_FINISH_LINE/2.0) { - const draft_int_lo = parseInt(Math.round((contest_i as any)/10.0) as any); - if (isNaN(this.bpm_contest_lo[draft_int_lo])) this.bpm_contest_lo[draft_int_lo] = 0; - this.bpm_contest_lo[draft_int_lo]+= (this.bpm_contest[contest_i]/6.0)*this.last_update; + if (contest_max < this.bpm_contest[contest_i]) + contest_max = this.bpm_contest[contest_i]; + if (this.bpm_contest[contest_i] > this.config.BD_FINISH_LINE / 2.0) { + const draft_int_lo = parseInt( + Math.round((contest_i as any) / 10.0) as any + ); + if (isNaN(this.bpm_contest_lo[draft_int_lo])) + this.bpm_contest_lo[draft_int_lo] = 0; + this.bpm_contest_lo[draft_int_lo] += + (this.bpm_contest[contest_i] / 6.0) * this.last_update; } } // normalize to a finish line if (contest_max > this.config.BD_FINISH_LINE) { for (const contest_i in this.bpm_contest) { - this.bpm_contest[contest_i]=(this.bpm_contest[contest_i]/contest_max)*this.config.BD_FINISH_LINE; + this.bpm_contest[contest_i] = + (this.bpm_contest[contest_i] / contest_max) * + this.config.BD_FINISH_LINE; } } contest_max = 0; for (const contest_i in this.bpm_contest_lo) { - if (contest_max < this.bpm_contest_lo[contest_i]) contest_max = this.bpm_contest_lo[contest_i]; + if (contest_max < this.bpm_contest_lo[contest_i]) + contest_max = this.bpm_contest_lo[contest_i]; } // normalize to a finish line if (contest_max > this.config.BD_FINISH_LINE) { for (const contest_i in this.bpm_contest_lo) { - this.bpm_contest_lo[contest_i]=(this.bpm_contest_lo[contest_i]/contest_max)*this.config.BD_FINISH_LINE; + this.bpm_contest_lo[contest_i] = + (this.bpm_contest_lo[contest_i] / contest_max) * + this.config.BD_FINISH_LINE; } } - // decay contest values from last loop for (const contest_i in this.bpm_contest) { - this.bpm_contest[contest_i]-=this.bpm_contest[contest_i]*(this.last_update/this.config.BD_DETECTION_RATE); + this.bpm_contest[contest_i] -= + this.bpm_contest[contest_i] * + (this.last_update / this.config.BD_DETECTION_RATE); } // decay contest values from last loop for (const contest_i in this.bpm_contest_lo) { - this.bpm_contest_lo[contest_i]-=this.bpm_contest_lo[contest_i]*(this.last_update/this.config.BD_DETECTION_RATE); + this.bpm_contest_lo[contest_i] -= + this.bpm_contest_lo[contest_i] * + (this.last_update / this.config.BD_DETECTION_RATE); } - this.bpm_timer+=this.last_update; + this.bpm_timer += this.last_update; let winner; let winner_lo; // attempt to display the beat at the beat interval ;) - if (this.bpm_timer > this.winning_bpm/4.0 && this.current_bpm) { + if (this.bpm_timer > this.winning_bpm / 4.0 && this.current_bpm) { this.win_val = 0; this.win_val_lo = 0; - if (this.winning_bpm) while (this.bpm_timer > this.winning_bpm/4.0) this.bpm_timer -= this.winning_bpm/4.0; + if (this.winning_bpm) + while (this.bpm_timer > this.winning_bpm / 4.0) + this.bpm_timer -= this.winning_bpm / 4.0; // increment beat counter this.quarter_counter++; - this.half_counter= Math.floor(this.quarter_counter/2); - this.beat_counter = Math.floor(this.quarter_counter/4); + this.half_counter = Math.floor(this.quarter_counter / 2); + this.beat_counter = Math.floor(this.quarter_counter / 4); // award the winner of this iteration - const idx = Math.floor(Math.round((60.0/this.current_bpm)*10.0)); + const idx = Math.floor(Math.round((60.0 / this.current_bpm) * 10.0)); if (isNaN(this.bpm_contest[idx])) this.bpm_contest[idx] = 0; - this.bpm_contest[idx]+=this.config.BD_QUALITY_REWARD; + this.bpm_contest[idx] += this.config.BD_QUALITY_REWARD; // find the overall winner so far for (const contest_i in this.bpm_contest) { @@ -798,7 +908,7 @@ class BeatsDetektor { if (winner) { this.win_bpm_int = parseInt(winner); - this.winning_bpm = (60.0/(winner/10.0)); + this.winning_bpm = 60.0 / (winner / 10.0); } // find the overall winner so far @@ -811,10 +921,10 @@ class BeatsDetektor { if (winner_lo) { this.win_bpm_int_lo = parseInt(winner_lo); - this.winning_bpm_lo = 60.0/winner_lo; + this.winning_bpm_lo = 60.0 / winner_lo; } - if (console && (this.beat_counter % 4) == 0) { + if (console && this.beat_counter % 4 == 0) { // console.log(`Bea.ts(${this.BPM_MIN}, ${this.BPM_MAX}): [ Current Estimate: ${winner} BPM ] [ Time: ${Math.floor(timer_seconds*1000.0)/1000.0}s, Quality: ${Math.floor(this.quality_total*1000.0)/1000.0}, Rank: ${Math.floor(this.win_val*1000.0)/1000.0}, Jitter: ${Math.floor(this.bpm_offset*1000000.0)/1000000.0} ]`); } } @@ -824,9 +934,9 @@ class BeatsDetektor { } /** -* simple bass kick visualizer assistant module -* @public -*/ + * simple bass kick visualizer assistant module + * @public + */ // class BeatsKick { // is_kick = false; @@ -847,10 +957,9 @@ class BeatsDetektor { // } // } - /** -* simple vu spectrum visualizer assistant module -*/ + * simple vu spectrum visualizer assistant module + */ // class BeatSpect { // vu_levels = []; @@ -900,5 +1009,3 @@ class BeatsDetektor { // return this.vu_levels[x]; // } // } - - diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index bdcfe6c..6e7fa15 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -1,24 +1,34 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ - -import {CComponent, CSettings, Smallog, waitReady, WascInterface, wascWorker, WascLoader, ASUtil} from '../'; - -import {Bea_ts} from './Bea'; + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ + +import { + CComponent, + CSettings, + Smallog, + waitReady, + WascInterface, + wascWorker, + WascLoader, + ASUtil, + sharedWorker, +} from "../"; + +import { Bea_ts } from "./Bea"; const DAT_LEN = 128; -const LISTENAME = 'wallpaperRegisterAudioListener'; +const LISTENAME = "wallpaperRegisterAudioListener"; /** -* Audio processing settings -* @public -* @extends {CSettings} -*/ + * Audio processing settings + * @public + * @extends {CSettings} + */ export class WEASettings extends CSettings { debugging = false; /** do audio processing? */ @@ -49,9 +59,9 @@ export class WEASettings extends CSettings { } /** -* Processed audio data representation -* @public -*/ + * Processed audio data representation + * @public + */ export type WEAudio = { time: number; ellapsed: number; @@ -66,15 +76,17 @@ export type WEAudio = { range: number; silent: number; intensity: number; - bpm?: [{ - value: number, - weight: number - }] -} + bpm?: [ + { + value: number; + weight: number; + } + ]; +}; /** -* @public -*/ + * @public + */ const SettIDs = { equalize: 0, mono_audio: 1, @@ -90,8 +102,8 @@ const SettIDs = { }; /** -* @public -*/ + * @public + */ const PropIDs = { bass: 0, mids: 1, @@ -106,21 +118,21 @@ const PropIDs = { }; /** -* WEAS -*
      -* Wallpaper Engine Audio Supplier makes working with audio easier. -*
      -* It will automatically start to receive and process the audio data -* which can then be accessed on the global object. -*
      -* DEPENDS ON: -*
      -* - Wallpaper Engine Web Wallpaper environment -*
      -* - audio-processing supported web wallpaper -* @public -* @extends {CComponent} -*/ + * WEAS + *
      + * Wallpaper Engine Audio Supplier makes working with audio easier. + *
      + * It will automatically start to receive and process the audio data + * which can then be accessed on the global object. + *
      + * DEPENDS ON: + *
      + * - Wallpaper Engine Web Wallpaper environment + *
      + * - audio-processing supported web wallpaper + * @public + * @extends {CComponent} + */ export class WEAS extends CComponent { /** @public last processed audio object */ public lastAudio: WEAudio = null; @@ -145,14 +157,12 @@ export class WEAS extends CComponent { context2: CanvasRenderingContext2D; display: HTMLElement; - sharedASUtils: ASUtil; - public init?: () => Promise; /** - * delay audio initialization until page ready - * @param {boolean} detectBpm (optional) - */ + * delay audio initialization until page ready + * @param {boolean} detectBpm (optional) + */ constructor(autoInit = false, detectBpm = false) { super(); if (detectBpm) this.bpModule = new Bea_ts(); @@ -160,7 +170,6 @@ export class WEAS extends CComponent { else this.init = this.realInit; } - /** * convert float based audio to int * @param {Array} data @@ -176,14 +185,14 @@ export class WEAS extends CComponent { } /** - * initializes audio processing pipeline - * and starts listening on audio data - * @ignore - */ + * initializes audio processing pipeline + * and starts listening on audio data + * @ignore + */ private async realInit() { // only listen if wallpaper engine context given if (!window[LISTENAME]) { - Smallog.error('\'window.wallpaperRegisterAudioListener\' not given!'); + Smallog.error("'window.wallpaperRegisterAudioListener' not given!"); return; } this.init = null; @@ -191,24 +200,24 @@ export class WEAS extends CComponent { this.injectCSS(); this.injectHTML(); - const iso = window['crossOriginIsolated'] === true; - wascWorker('WEAS.wasm', 4096, iso, {}, !this.settings.low_latency).then((myModule) => { - this.weasModule = myModule; + sharedWorker("WEAS.wasm", 4096, {}, !this.settings.low_latency) + .then((myModule) => { + this.weasModule = myModule; - if (myModule.sharedMemory) { - this.sharedASUtils = new WascLoader().postInstantiate({}, {exports: {memory: myModule.sharedMemory}} as any); - Smallog.debug('Got shared memory access to WebAssembly audio!'); - } + if (myModule.shared) { + Smallog.debug("Got shared WebAssembly audio!"); + } - this.updateSettings().then(() => { - this.registerListener(); - Smallog.debug('WebAssembly Audio provider is ready!'); + this.updateSettings().then(() => { + this.registerListener(); + Smallog.debug("WebAssembly Audio provider is ready!"); + }); + }) + .catch((err) => { + const m = `Could not create WebAssembly Audio provider! ErrMsg: ${err}`; + Smallog.error(m); + alert(m); }); - }).catch((err) => { - const m = `Could not create WebAssembly Audio provider! ErrMsg: ${err}`; - Smallog.error(m); - alert(m); - }); } /** @@ -216,63 +225,77 @@ export class WEAS extends CComponent { * @ignore */ private registerListener() { - const {run} = this.weasModule; - const self = this; - // register audio callback on module window[LISTENAME]((audioArray) => { // Smallog.debug('Get Audio Data!'); // check basic - if (!self.settings.audioprocessing || audioArray == null || audioArray.length != DAT_LEN) { - Smallog.error('received invalid audio data: ' + JSON.stringify([audioArray.length || null, audioArray])); + if ( + !this.settings.audioprocessing || + audioArray == null || + audioArray.length != DAT_LEN + ) { + Smallog.error( + "received invalid audio data: " + + JSON.stringify([audioArray.length || null, audioArray]) + ); return; } // prepare data const start = performance.now(); - self.inBuff.set(audioArray); + this.inBuff.set(audioArray); // WRAP IN isolated Function ran inside worker - run(({module, instance, exports, params}) => { - const ex = instance.exports as any; - const {data} = params[0]; - const arrData = new Float64Array(data); - - // set audio data directly in module memory - exports.__getFloat64ArrayView(ex.inputData).set(arrData); - // trigger processing - ex.update(); - // get copy of webassembly data - const r = { - // ptr: {data: ex.outputData, props: ex.audioProps}, - data: new Float64Array(exports.__getFloat64ArrayView(ex.outputData)).buffer, - props: new Float64Array(exports.__getFloat64ArrayView(ex.audioProps)).buffer, - }; - return r; - }, - { - data: self.inBuff.buffer, - }) + this.weasModule + .run( + ({ module, instance, exports, params }) => { + const ex = instance.exports as any; + const { data } = params[0]; + const arrData = new Float64Array(data); + + // set audio data directly in module memory + exports.__getFloat64ArrayView(ex.inputData).set(arrData); + // trigger processing + ex.update(); + // get copy of webassembly data + const r = { + // ptr: {data: ex.outputData, props: ex.audioProps}, + data: new Float64Array( + exports.__getFloat64ArrayView(ex.outputData) + ).buffer, + props: new Float64Array( + exports.__getFloat64ArrayView(ex.audioProps) + ).buffer, + }; + return r; + }, + { + data: this.inBuff.buffer, + } + ) .then((result) => { // worker result, back in main context - const {data, props} = result; + const { data, props } = result; const arrData = new Float64Array(data); const arrProps = new Float64Array(props); - const realProps = self.getProps(arrProps); + const realProps = this.getProps(arrProps); const teim = performance.now() - start; - if (self.lastAudio && !self.lastAudio.silent && realProps.silent) { + if (this.lastAudio && !this.lastAudio.silent && realProps.silent) { console.log(audioArray, arrData, arrProps); } let bpmObj = {}; if (this.bpModule && !realProps.silent) { - bpmObj = this.bpModule.process(start / 1000.0, this.convertAudio(audioArray)); + bpmObj = this.bpModule.process( + start / 1000.0, + this.convertAudio(audioArray) + ); } // apply actual last Data from worker - self.lastAudio = { + this.lastAudio = { time: start, ellapsed: teim, data: arrData, @@ -284,11 +307,11 @@ export class WEAS extends CComponent { } /** - * Inject preview CSS - * @ignore - */ + * Inject preview CSS + * @ignore + */ private injectCSS() { - const st = document.createElement('style'); + const st = document.createElement("style"); st.innerHTML = ` #weas-preview { opacity: 0; @@ -312,12 +335,12 @@ export class WEAS extends CComponent { } /** - * Inject preview canvas - * @ignore - */ + * Inject preview canvas + * @ignore + */ private injectHTML() { - this.mainElm = document.createElement('div'); - this.mainElm.id = 'weas-preview'; + this.mainElm = document.createElement("div"); + this.mainElm.id = "weas-preview"; this.mainElm.innerHTML = `
      Raw Data: @@ -333,23 +356,23 @@ export class WEAS extends CComponent { `; document.body.append(this.mainElm); - this.canvas1 = (document.getElementById('WEASCanvas1') as HTMLCanvasElement); + this.canvas1 = document.getElementById("WEASCanvas1") as HTMLCanvasElement; this.canvas1.width = window.innerWidth; - this.context1 = this.canvas1.getContext('2d'); + this.context1 = this.canvas1.getContext("2d"); - this.canvas2 = (document.getElementById('WEASCanvas2') as HTMLCanvasElement); + this.canvas2 = document.getElementById("WEASCanvas2") as HTMLCanvasElement; this.canvas2.width = window.innerWidth; - this.context2 = this.canvas2.getContext('2d'); + this.context2 = this.canvas2.getContext("2d"); - this.display = document.getElementById('WEASDisplay'); + this.display = document.getElementById("WEASDisplay"); } /** - * converts calculated output property number-array to string-associative-array - * @param {ArrayLike} dProps processed properties - * @return {any} - * @ignore - */ + * converts calculated output property number-array to string-associative-array + * @param {ArrayLike} dProps processed properties + * @return {any} + * @ignore + */ private getProps(dProps: ArrayLike) { const keys = Object.keys(PropIDs); const res = {}; @@ -361,25 +384,25 @@ export class WEAS extends CComponent { } /** - * !! CAVEAT: only available after init and module load !! - *
      - * Will send the processing settings to the WebAssembly module - * @public - * @return {Promise} finished event - */ + * !! CAVEAT: only available after init and module load !! + *
      + * Will send the processing settings to the WebAssembly module + * @public + * @return {Promise} finished event + */ public updateSettings(): Promise { if (!this.weasModule) return; // show or hide debug info if (this.settings.debugging) { - if (!this.mainElm.classList.contains('show')) { - this.mainElm.classList.add('show'); + if (!this.mainElm.classList.contains("show")) { + this.mainElm.classList.add("show"); } } else { - this.mainElm.classList.remove('show'); + this.mainElm.classList.remove("show"); } - const {run} = this.weasModule; + const { run } = this.weasModule; return new Promise((resolve) => { const keys = Object.keys(SettIDs); @@ -390,78 +413,100 @@ export class WEAS extends CComponent { } // isolated Function running inside worker - run(({module, instance, exports, params}) => { - const ex = instance.exports as any; - const {data} = params[0]; - const arrDat = new Float64Array(data); - exports.__getFloat64ArrayView(ex.audioSettings).set(arrDat); - }, - // Data passed to worker - { - data: sett.buffer, - }) + run( + ({ module, instance, exports, params }) => { + const ex = instance.exports as any; + const { data } = params[0]; + const arrDat = new Float64Array(data); + exports.__getFloat64ArrayView(ex.audioSettings).set(arrDat); + }, + // Data passed to worker + { + data: sett.buffer, + } + ) // Back to main context .then(() => { - Smallog.debug('Sent Settings to WEAS: ' + JSON.stringify(sett)); + Smallog.debug("Sent Settings to WEAS: " + JSON.stringify(sett)); resolve(); }); }); } /** - * @return {boolean} false if: - *
      - * - processing is disabled - *
      - * - there is no data - *
      - * - the data is silent - *
      - * - data is too old (> 3s) - * @public - */ + * @return {boolean} false if: + *
      + * - processing is disabled + *
      + * - there is no data + *
      + * - the data is silent + *
      + * - data is too old (> 3s) + * @public + */ public hasAudio() { if (this.settings.show_canvas) this.updateCanvas(); - return this.settings.audioprocessing && - this.lastAudio && this.lastAudio.silent == 0 && - (performance.now() - this.lastAudio.time < 3000); + return ( + this.settings.audioprocessing && + this.lastAudio && + this.lastAudio.silent <= 0 && + performance.now() - this.lastAudio.time < 3000 + ); } /** - * Update the debug canvas - */ + * Update the debug canvas + */ private updateCanvas() { // update "raw" canvas // clear the intersection this.context1.clearRect(0, 0, this.canvas1.width, this.canvas1.height); - this.context1.fillStyle = 'rgb(255,0,0)'; - const barWidth = Math.round(1.0 / this.inBuff.length * this.canvas1.width); + this.context1.fillStyle = "rgb(255,0,0)"; + const barWidth = Math.round( + (1.0 / this.inBuff.length) * this.canvas1.width + ); const halfCount = this.inBuff.length / 2; for (let i = 0; i < halfCount; ++i) { - const height = this.canvas1.height * this.inBuff[i] / 2; - this.context1.fillRect(barWidth * i, this.canvas1.height - height, barWidth, height); + const height = (this.canvas1.height * this.inBuff[i]) / 2; + this.context1.fillRect( + barWidth * i, + this.canvas1.height - height, + barWidth, + height + ); } - this.context1.fillStyle = 'rgb(0,0,255)'; + this.context1.fillStyle = "rgb(0,0,255)"; for (let i = halfCount; i < this.inBuff.length; ++i) { - const height = this.canvas1.height * this.inBuff[191 - i] / 2; - this.context1.fillRect(barWidth * i, this.canvas1.height - height, barWidth, height); + const height = (this.canvas1.height * this.inBuff[191 - i]) / 2; + this.context1.fillRect( + barWidth * i, + this.canvas1.height - height, + barWidth, + height + ); } // update "processed" data const tmpData = Object.assign({}, this.lastAudio); tmpData.data = null; - this.display.innerText = JSON.stringify(tmpData, null, '\t'); + this.display.innerText = JSON.stringify(tmpData, null, "\t"); // update "processed" canvas this.context2.clearRect(0, 0, this.canvas2.width, this.canvas2.height); if (this.lastAudio && this.lastAudio.data) { - this.context2.fillStyle = 'rgb(0,255,0)'; + this.context2.fillStyle = "rgb(0,255,0)"; for (let index = 0; index < this.lastAudio.data.length; index++) { - const height = this.canvas2.height * this.lastAudio.data[index] / 2; - this.context2.fillRect(barWidth * index, this.canvas2.height - height, barWidth, height); + const height = (this.canvas2.height * this.lastAudio.data[index]) / 2; + this.context2.fillRect( + barWidth * index, + this.canvas2.height - height, + barWidth, + height + ); } /* // draw average, min & max lines diff --git a/src/weas/WEAS.wasm.asc b/src/weas/WEAS.wasm.asc index 0e67a02..ee6a9f0 100644 --- a/src/weas/WEAS.wasm.asc +++ b/src/weas/WEAS.wasm.asc @@ -151,23 +151,27 @@ function equalizePeaks(array: Float64Array): void { if(array[i] >= peakData[i]) { peakData[i] = array[i]; peakStrength[i] = 16; - } + // min is less worth now + minStrength[i] = minStrength[i] / 2; + } else { + // peak decrementing if too weak if(peakStrength[i] < 1) peakData[i] *= 0.97; else peakStrength[i] -= (peakData[i] - array[i]); } // min edging if(array[i] <= minData[i]) { minData[i] = array[i] * 0.9; - minStrength[i] = 16; + minStrength[i] = 8; } else { + // min incrementing if too wek if(minStrength[i] < 1) minData[i] *= 1.01; else minStrength[i] -= (array[i] - minData[i]); } // calculate anti dynamics -> high frequencies = more - tmpF = 0.3 + ((i as f64) / (DAT_LEN as f64)) * 0.4; + tmpF = 0.3 + ((i as f64) / (DAT_LEN as f64)) * 0.2; // scale equalize emult = (array[i] - minData[i]) / (peakData[i] - minData[i]); diff --git a/src/weas/index.ts b/src/weas/index.ts index 1de0d39..d505a09 100644 --- a/src/weas/index.ts +++ b/src/weas/index.ts @@ -1,12 +1,12 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -* -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + */ -export * from './WEAS'; -export * from './Bea'; +export * from "./WEAS"; +export * from "./Bea"; diff --git a/src/worker-loader.d.ts b/src/worker-loader.d.ts index e76049c..f0057ee 100644 --- a/src/worker-loader.d.ts +++ b/src/worker-loader.d.ts @@ -1,30 +1,29 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ /** -* This is just a definition wrapper. -* -* @see https://github.com/webpack-contrib/worker-loader -*/ -declare module 'worker-loader!*' { - + * This is just a definition wrapper. + * + * @see https://github.com/webpack-contrib/worker-loader + */ +declare module "worker-loader!*" { /** - * You need to change `Worker`, if: - * you specified a different value for the `workerType` option - */ + * You need to change `Worker`, if: + * you specified a different value for the `workerType` option + */ class WebpackWorker extends Worker { constructor(options?: any); } /** - * Change this if you set the `esModule` option to `false` - * // export = WebpackWorker; - */ + * Change this if you set the `esModule` option to `false` + * // export = WebpackWorker; + */ export default WebpackWorker; } From ac14227f6df84390a10c01f2c5beaa3e115b2556 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 5 Mar 2022 15:25:52 +0100 Subject: [PATCH 49/76] Fix: submodules --- src/Util.ts | 16 -- src/XRHelper.ts | 2 +- src/XRWebGL.d.ts | 385 ----------------------------------- src/offline/OfflinePlugin.js | 177 ++++++++-------- src/three.ts | 2 +- src/wasc-worker | 2 +- 6 files changed, 88 insertions(+), 496 deletions(-) delete mode 100644 src/XRWebGL.d.ts diff --git a/src/Util.ts b/src/Util.ts index 751a7bd..9eee392 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -114,19 +114,3 @@ export function sharedWorker( if (iso) bldr = bldr.replace(".wasm", ".shared.wasm"); return wascWorker(bldr, memory, iso, options, useWorker); } - -/** - * Shorthand for Typed array stuff - * @public - */ -export const AnyTypedArray: - | Uint32Array - | Float32Array - | Float64Array - | Uint8Array - | Int8Array - | Uint16Array - | Int16Array - | Int32Array - | BigUint64Array - | BigInt64Array = null; diff --git a/src/XRHelper.ts b/src/XRHelper.ts index a6ed7b3..afdb58e 100644 --- a/src/XRHelper.ts +++ b/src/XRHelper.ts @@ -5,7 +5,7 @@ */ import { CSettings, CComponent, waitReady } from "./"; -import { XRSessionInit } from "./XRWebGL"; +import { XRSessionInit } from "./three.ts/src/XRWebGL"; /** * XR Settings diff --git a/src/XRWebGL.d.ts b/src/XRWebGL.d.ts deleted file mode 100644 index dd10077..0000000 --- a/src/XRWebGL.d.ts +++ /dev/null @@ -1,385 +0,0 @@ -/* eslint-disable @typescript-eslint/consistent-type-definitions */ - -/* eslint-disable no-unused-vars */ - -// # General utilities - -type EventHandlerNonNull = (event: Event) => void; - -type EventHandler = EventHandlerNonNull | null | undefined; - -// DOMString is not exactly a string (https://heycam.github.io/webidl/#idl-DOMString). - -// (TODO) - -type DOMString = string; - -// # WebXR - -export type XRSessionMode = "inline" | "immersive-vr" | "immersive-ar"; - -export type XRReferenceSpaceType = - | "viewer" - | "local" - | "local-floor" - | "bounded-floor" - | "unbounded"; - -export type XRVisibilityState = "visible" | "visible-blurred" | "hidden"; - -export type XREye = "none" | "left" | "right"; - -export type XRHandedness = "none" | "left" | "right"; - -export type XRTargetRayMode = "gaze" | "tracked-pointer" | "screen"; - -export type XRWebGLRenderingContext = - | WebGLRenderingContext - | WebGL2RenderingContext; - -export interface XRSessionInit { - requiredFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) - - optionalFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) -} - -declare global { - export class XRRigidTransform { - constructor(position?: DOMPointInit, orientation?: DOMPointInit); - - readonly position: DOMPointReadOnly; - - readonly orientation: DOMPointReadOnly; - - readonly matrix: Float32Array; - - readonly inverse: XRRigidTransform; - } - - export interface XRViewport { - readonly x: number; - - readonly y: number; - - readonly width: number; - - readonly height: number; - } -} - -// ## Events - -export interface XRSessionEventInit extends EventInit { - session: XRSession; -} - -export interface XRInputSourceEventInit extends EventInit { - frame: XRFrame; - - inputSource: XRInputSource; -} - -export interface XRInputSourcesChangeEventInit extends EventInit { - session: XRSession; - - added: XRInputSource[]; // FrozenArray (TODO?) - - removed: XRInputSource[]; // FrozenArray (TODO?) -} - -export interface XRReferenceSpaceEventInit extends EventInit { - referenceSpace: XRReferenceSpace; - - transform?: XRRigidTransform | null; -} - -declare global { - export class XRSessionEvent extends Event { - constructor(type: DOMString, eventInitDict: XRSessionEventInit); - - readonly session: XRSession; - } - - export class XRInputSourceEvent extends Event { - constructor(type: DOMString, eventInitDict: XRInputSourceEventInit); - - readonly frame: XRFrame; - - readonly inputSource: XRInputSource; - } - - export class XRInputSourcesChangeEvent extends Event { - constructor(type: DOMString, eventInitDict: XRInputSourcesChangeEventInit); - - readonly session: XRSession; - - readonly added: XRInputSource[]; // FrozenArray (TODO?) - - readonly removed: XRInputSource[]; // FrozenArray (TODO?) - } - - export class XRReferenceSpaceEvent extends Event { - constructor(type: DOMString, eventInitDict: XRReferenceSpaceEventInit); - - readonly referenceSpace: XRReferenceSpace; - - readonly transform: XRRigidTransform | null; - } -} - -// ## Permissions - -export interface XRPermissionDescriptor extends PermissionDescriptor { - mode?: XRSessionMode; - - requiredFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) - - optionalFeatures?: XRReferenceSpaceType[]; // sequence (TODO?) -} - -declare global { - export interface XRPermissionStatus extends PermissionStatus { - granted: []; // FrozenArray (TODO?) ; (TODO) Also is this really `any`? - } - - // ## Input sources - - export interface XRInputSource { - readonly handedness: XRHandedness; - - readonly targetRayMode: XRTargetRayMode; - - readonly targetRaySpace: XRSpace; - - readonly gripSpace: XRSpace | null; - - readonly profiles: DOMString[]; // FrozenArray in the doc (TODO?) - } - - // This is actually a novel data structure which emulates a JS array (e.g. getter + `.length`) - - // but it is not an array (TODO) - - export type XRInputSourceArray = XRInputSource[]; - - // ## View - - export interface XRView { - readonly eye: XREye; - - readonly projectionMatrix: Float32Array; - - readonly transform: XRRigidTransform; - } - - // ## Spaces - - // eslint-disable-next-line @typescript-eslint/no-empty-interface - export interface XRSpace extends EventTarget {} - - export interface XRReferenceSpace extends XRSpace { - getOffsetReferenceSpace(originOffset: XRRigidTransform): XRReferenceSpace; - - onreset: EventHandler; - } - - export interface XRBoundedReferenceSpace extends XRReferenceSpace { - readonly boundsGeometry: DOMPointReadOnly[]; // FrozenArray (TODO?) - } - - // ## Poses - - export interface XRPose { - readonly transform: XRRigidTransform; - - readonly emulatedPosition: boolean; - } - - export interface XRViewerPose extends XRPose { - readonly views: XRView[]; // FrozenArray in the docs (TODO?) - } - - // ## Frames - - export interface XRFrame { - readonly session: XRSession; - - getViewerPose(referenceSpace: XRReferenceSpace): XRViewerPose | null; - - getPose(space: XRSpace, baseSpace: XRSpace): XRPose | null; - } -} - -export type XRFrameRequestCallback = ( - time: DOMHighResTimeStamp, - - frame: XRFrame -) => void; - -// ## WebGL interop - -export interface XRWebGLLayerInit { - antialias?: boolean; - - depth?: boolean; - - stencil?: boolean; - - alpha?: boolean; - - ignoreDepthValues?: boolean; - - framebufferScaleFactor?: number; -} - -declare global { - export class XRWebGLLayer { - constructor( - session: XRSession, - - context: XRWebGLRenderingContext, - - layerInit?: XRWebGLLayerInit - ); - - readonly antialias: boolean; - - readonly ignoreDepthValues: boolean; - - readonly framebuffer?: WebGLFramebuffer; - - readonly framebufferWidth: number; - - readonly framebufferHeight: number; - - getViewport(view: XRView): XRViewport | null; - - static getNativeFramebufferScaleFactor(session: XRSession): number; - } -} - -// ## Session - -export interface XRRenderStateInit { - depthNear?: number; - - depthFar?: number; - - inlineVerticalFieldOfView?: number; - - baseLayer?: XRWebGLLayer | null; -} - -declare global { - export interface XRRenderState { - readonly depthNear: number; - - readonly depthFar: number; - - readonly inlineVerticalFieldOfView?: number; - - readonly baseLayer?: XRWebGLLayer; - } - - export interface XRSession extends EventTarget { - readonly visibilityState: XRVisibilityState; - - readonly renderState: XRRenderState; - - readonly inputSources: XRInputSourceArray; - - // Methods - - updateRenderState(state?: XRRenderStateInit): void; - - requestReferenceSpace( - type: XRReferenceSpaceType - ): Promise; - - requestAnimationFrame(callback: XRFrameRequestCallback): number; - - cancelAnimationFrame(handle: number): void; - - end(): Promise; - - environmentBlendMode: string; - - // Events - - onend: EventHandler; - - oninputsourceschange: EventHandler; - - onselect: EventHandler; - - onselectstart: EventHandler; - - onselectend: EventHandler; - - onsqueeze: EventHandler; - - onsqueezestart: EventHandler; - - onsqueezeend: EventHandler; - - onvisibilitychange: EventHandler; - } - - // ## System - - export interface XRSystem extends EventTarget { - isSessionSupported(mode: XRSessionMode): Promise; - - requestSession( - mode: XRSessionMode, - - options?: XRSessionInit - ): Promise; - - ondevicechange: EventHandler; - - addEventListener( - type: "devicechange", - - listener: EventListenerOrEventListenerObject, - - options?: boolean | AddEventListenerOptions - ): void; - } - - // ## Updates to existing objects - - interface Navigator { - /** - - * Optional because WebXR support is limited across browsers - - */ - - xr?: XRSystem; - } - - interface GLESRenderingContext { - /** - - * Optional because WebXR support is limited across browsers - - */ - - makeXRCompatible?(): Promise; - } - - interface Window { - XRRigidTransform?: typeof XRRigidTransform; - - XRWebGLLayer?: typeof XRWebGLLayer; - - XRSessionEvent?: typeof XRSessionEvent; - - XRInputSourceEvent?: typeof XRInputSourceEvent; - - XRInputSourcesChangeEvent?: typeof XRInputSourcesChangeEvent; - - XRReferenceSpaceEvent?: typeof XRReferenceSpaceEvent; - } -} diff --git a/src/offline/OfflinePlugin.js b/src/offline/OfflinePlugin.js index 5568ab1..d770138 100644 --- a/src/offline/OfflinePlugin.js +++ b/src/offline/OfflinePlugin.js @@ -20,21 +20,21 @@ const pluginName = "OfflinePlugin"; * @see {OfflinePlugin} */ const offlineSchema = { - type: "object", - properties: { - staticdir: { - type: "string", - }, - outfile: { - type: "string", - }, - extrafiles: { - type: "array", - }, - pretty: { - type: "boolean", - }, - }, + type: "object", + properties: { + staticdir: { + type: "string", + }, + outfile: { + type: "string", + }, + extrafiles: { + type: "array", + }, + pretty: { + type: "boolean", + }, + }, }; /** @@ -45,22 +45,18 @@ const offlineSchema = { * @return {array} arrayOfFiles */ function getAllFiles(baseDir, subDir, arrayOfFiles) { - const sub = baseDir + "/" + subDir; - const files = fs.readdirSync(sub); - arrayOfFiles = arrayOfFiles || []; - files.forEach((file) => { - const fle = sub + "/" + file; - if (fs.statSync(fle).isDirectory()) { - arrayOfFiles = getAllFiles( - baseDir, - subDir + "/" + file, - arrayOfFiles - ); - } else { - arrayOfFiles.push(subDir + "/" + file); - } - }); - return arrayOfFiles; + const sub = baseDir + "/" + subDir; + const files = fs.readdirSync(sub); + arrayOfFiles = arrayOfFiles || []; + files.forEach((file) => { + const fle = sub + "/" + file; + if (fs.statSync(fle).isDirectory()) { + arrayOfFiles = getAllFiles(baseDir, subDir + "/" + file, arrayOfFiles); + } else { + arrayOfFiles.push(subDir + "/" + file); + } + }); + return arrayOfFiles; } /** @@ -88,76 +84,73 @@ function getAllFiles(baseDir, subDir, arrayOfFiles) { * You can also ignore this plugin and create the 'offlinefiles.json' yourself. */ class OfflinePlugin { - options = {}; + options = {}; - /** - * Intializes the plugin in the webpack build process - * @param {offliineSchema} options - */ - constructor(options = {}) { - validate.validate(offlineSchema, options); - this.options = options; - } + /** + * Intializes the plugin in the webpack build process + * @param {offliineSchema} options + */ + constructor(options = {}) { + validate.validate(offlineSchema, options); + this.options = options; + } - /** - * Hook into the compilation process, - * find all target files and put them into a json file - * @param {Webpack.compiler} compiler object from webpack - */ - apply(compiler) { - let addedOnce = false; - // Specify the event hook to attach to - compiler.hooks.thisCompilation.tap(pluginName, (compilation) => - compilation.hooks.processAssets.tap( - { - name: pluginName, - stage: Compilation.PROCESS_ASSETS_STAGE_ANALYSE, - }, - () => { - if (addedOnce) return; - addedOnce = true; + /** + * Hook into the compilation process, + * find all target files and put them into a json file + * @param {Webpack.compiler} compiler object from webpack + */ + apply(compiler) { + let addedOnce = false; + // Specify the event hook to attach to + compiler.hooks.thisCompilation.tap(pluginName, (compilation) => + compilation.hooks.processAssets.tap( + { + name: pluginName, + stage: Compilation.PROCESS_ASSETS_STAGE_ANALYSE, + }, + () => { + if (addedOnce) return; + addedOnce = true; - console.info("[" + pluginName + "] Gathering Infos..."); + console.info("[" + pluginName + "] Gathering Infos..."); - // list of all app-contents - const filelist = []; + // list of all app-contents + const filelist = []; - // add static files from folder - const sFiles = getAllFiles(this.options.staticdir, ""); - for (const staticFile in sFiles) { - if (!staticFile) continue; - filelist.push(sFiles[staticFile]); - } + // add static files from folder + const sFiles = getAllFiles(this.options.staticdir, ""); + for (const staticFile in sFiles) { + if (!staticFile) continue; + filelist.push(sFiles[staticFile]); + } - // Loop through all compiled assets, - // adding a new line item for each filename. - for (const filename in compilation.assets) { - if (!filename) continue; - filelist.push("/" + filename); - } + // Loop through all compiled assets, + // adding a new line item for each filename. + for (const filename in compilation.assets) { + if (!filename) continue; + filelist.push("/" + filename); + } - // add additional files anyway? - if (this.options.extrafiles) { - this.options.extrafiles.map((ef) => filelist.push(ef)); - } + // add additional files anyway? + if (this.options.extrafiles) { + this.options.extrafiles.map((ef) => filelist.push(ef)); + } - // create the target file with all app-contents as json list - const jList = JSON.stringify( - filelist, - null, - this.options.pretty ? 1 : 0 - ); - compilation.emitAsset( - this.options.outfile, - new RawSource(jList) - ); - console.info("[" + pluginName + "] result: " + jList); + // create the target file with all app-contents as json list + const jList = JSON.stringify( + filelist, + null, + this.options.pretty ? 1 : 0 + ); + compilation.emitAsset(this.options.outfile, new RawSource(jList)); + console.info("[" + pluginName + "] result: " + jList); - console.info("[" + pluginName + "] finished."); - } - ) - ); - } + console.info("[" + pluginName + "] finished."); + } + ) + ); + } } module.exports = OfflinePlugin; diff --git a/src/three.ts b/src/three.ts index 22d43b6..23d6436 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 22d43b618ae19a0868eaac11175f3bf54bb13edc +Subproject commit 23d64363f84dc2426ac6c8a793ea650682abae68 diff --git a/src/wasc-worker b/src/wasc-worker index a041d6e..ddf2273 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit a041d6ef80a19fe61f07ff1455a0c5dacae69286 +Subproject commit ddf2273333ca5a104c8aa1cde8fefd37a6ccfeab From 150bb5234a831f34f6ec05488c75f20eef86b4e1 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sun, 6 Mar 2022 05:47:00 +0100 Subject: [PATCH 50/76] update submodule --- src/renamer/RenamerPlugin.js | 21 ++++++--------------- src/three.ts | 2 +- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js index 4bc9ecb..bc0d484 100644 --- a/src/renamer/RenamerPlugin.js +++ b/src/renamer/RenamerPlugin.js @@ -254,8 +254,7 @@ class RenamerPlugin { // check if replacement is in "" string // check if replacement is in '' string // check if replacement is in `` string - const patt = - /'((?:\\.|[^'])*)'|"((?:\\.|[^"])*)"|`((?:\\.|[^`])*)`/gim; + const patt = /'((?:\\.|[^'])*)'|"((?:\\.|[^"])*)"|`((?:\\.|[^`])*)`/gim; let match; while ((match = patt.exec(newPd)) != null && !isInvalid) { // console.log(match.index + ' ' + patt.lastIndex); @@ -347,9 +346,7 @@ class RenamerPlugin { let val = globalMap[k]; if (val.length > 4) { if (keys.length > 4) { - val = `${splFunc}('${btoa( - caesarCipher(val.join(delim)) - )}')`; + val = `${splFunc}('${btoa(caesarCipher(val.join(delim)))}')`; } else { val = `"${val.join(delim)}".split('${delim}')`; } @@ -413,7 +410,7 @@ class RenamerPlugin { * @return {void} */ processSource(compilation, name, child) { - if (child._valueIsBuffer) { + if (child._valueIsBuffer || !child.source) { console.log("Value is Buffer!"); return; } @@ -425,9 +422,7 @@ class RenamerPlugin { compilation.updateAsset(name, new RawSource(processed)); // calculate saved memory const savedChars = source.length - processed.length; - console.info( - "[" + pluginName + "] Saved: " + savedChars + " chars" - ); + console.info("[" + pluginName + "] Saved: " + savedChars + " chars"); } } @@ -440,16 +435,12 @@ class RenamerPlugin { apply(compiler) { compiler.hooks.emit.tap(pluginName, (compilation) => { try { - console.info( - "[" + pluginName + "] Using Regex: " + this.options.regex - ); + console.info("[" + pluginName + "] Using Regex: " + this.options.regex); // process all compiled .js files for (const assetFile in compilation.assets) { if (!assetFile || !assetFile.endsWith(".js")) continue; - console.info( - "[" + pluginName + "] Processing: " + assetFile - ); + console.info("[" + pluginName + "] Processing: " + assetFile); // get the processed asset object / source const asset = compilation.getAsset(assetFile); diff --git a/src/three.ts b/src/three.ts index 23d6436..288c8c3 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 23d64363f84dc2426ac6c8a793ea650682abae68 +Subproject commit 288c8c3d8bff62ec2f6d3c0b3956aea730597ecc From c8f9a7136cf970490b09b7d79f31caae77305a4d Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sun, 6 Mar 2022 09:08:34 +0100 Subject: [PATCH 51/76] fix default params --- src/three/EffectComposer.ts | 220 ++++++++++++++++++++++-------------- 1 file changed, 136 insertions(+), 84 deletions(-) diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts index dd47a4f..56198e6 100644 --- a/src/three/EffectComposer.ts +++ b/src/three/EffectComposer.ts @@ -1,23 +1,26 @@ /** -* @author alteredq / http://alteredqualia.com/ -* -* @author hexxone / https://hexx.one -*/ - -import {RenderPass, BasePass, LinearFilter, RGBAFormat, Scene, PerspectiveCamera, WebGLRenderer, Vector2, WebGLRenderTarget, Quaternion} from '../'; - - -const defaultParams = { - minFilter: LinearFilter, - magFilter: LinearFilter, - format: RGBAFormat, - stencilBuffer: false, -}; + * @author alteredq / http://alteredqualia.com/ + * + * @author hexxone / https://hexx.one + */ + +import { + RenderPass, + BasePass, + LinearFilter, + RGBAFormat, + Scene, + PerspectiveCamera, + WebGLRenderer, + Vector2, + WebGLRenderTarget, + Quaternion, +} from "../"; /** -* render shader chain -* @public -*/ + * render shader chain + * @public + */ export class EffectComposer { // given on construct private scene: Scene; @@ -46,31 +49,52 @@ export class EffectComposer { // public public passes: BasePass[] = []; - /** - * Instantiate - * @param {Scene} scene Scene - * @param {PerspectiveCamera} camera camera - * @param {WebGLRenderer} renderer renderer - * @param {string} globalPrec global precision - * @param {Color} clearCol Color - * @param {WebGLRenderTarget} renderTarget target to Reset (optional) - */ - constructor(scene: Scene, camera: PerspectiveCamera, renderer: WebGLRenderer, globalPrec = 'mediump', clearCol?: any, renderTarget?: WebGLRenderTarget) { + * Instantiate + * @param {Scene} scene Scene + * @param {PerspectiveCamera} camera camera + * @param {WebGLRenderer} renderer renderer + * @param {string} globalPrec global precision + * @param {Color} clearCol Color + * @param {WebGLRenderTarget} renderTarget target to Reset (optional) + */ + constructor( + scene: Scene, + camera: PerspectiveCamera, + renderer: WebGLRenderer, + globalPrec = "mediump", + clearCol?: any, + renderTarget?: WebGLRenderTarget + ) { this.scene = scene; this.camera = camera; this.renderer = renderer; this.viewSize = renderer.getSize(new Vector2()); - this.xrCam = new PerspectiveCamera(camera.fov, camera.aspect, camera.near, camera.far); + this.xrCam = new PerspectiveCamera( + camera.fov, + camera.aspect, + camera.near, + camera.far + ); // use a new default render target if none is given - this.defaultTarget = new WebGLRenderTarget(this.viewSize.width, this.viewSize.height, defaultParams); - this.defaultTarget.texture.name = 'EffectComposer.dt'; + this.defaultTarget = new WebGLRenderTarget( + this.viewSize.width, + this.viewSize.height, + { + minFilter: LinearFilter, + magFilter: LinearFilter, + format: RGBAFormat, + stencilBuffer: false, + } + ); + + this.defaultTarget.texture.name = "EffectComposer.dt"; if (renderTarget === undefined) { renderTarget = this.defaultTarget.clone(); - renderTarget.texture.name = 'EffectComposer.rt'; + renderTarget.texture.name = "EffectComposer.rt"; } // set write buffer for shader pass rendering @@ -79,7 +103,7 @@ export class EffectComposer { // set input buffer for shader pass rendering this.renderRead = renderTarget.clone(); - this.renderRead.texture.name = 'EffectComposer.rr'; + this.renderRead.texture.name = "EffectComposer.rr"; this.readBuffer = this.renderRead; this.passes = []; @@ -91,32 +115,32 @@ export class EffectComposer { } /** - * Precompile all shaders... - * @returns {void} - */ + * Precompile all shaders... + * @returns {void} + */ public precompile(): void { this.renderer.compile(this.scene, this.camera); this.passes.forEach((pass) => pass.prepare(this.renderer)); } /** - * Append a shader to the chain - * @public - * @param {BasePass} p Shader to add - * @returns {void} - */ + * Append a shader to the chain + * @public + * @param {BasePass} p Shader to add + * @returns {void} + */ public addPass(p: BasePass) { p.setSize(this.viewSize.width, this.viewSize.height); this.passes.push(p); } /** - * Insert a shader in the chain - * @public - * @param {BasePass} p Shader to add - * @param {number} index position - * @returns {void} - */ + * Insert a shader in the chain + * @public + * @param {BasePass} p Shader to add + * @param {number} index position + * @returns {void} + */ public insertPass(p: BasePass, index: number) { p.setSize(this.viewSize.width, this.viewSize.height); this.passes.splice(index, 0, p); @@ -125,7 +149,7 @@ export class EffectComposer { /** * Update clear color * @param {any} clearCol Color - * @returns {void} + * @returns {void} */ public setClearColor(clearCol: any) { this.normPass.clearColor = clearCol; @@ -133,11 +157,11 @@ export class EffectComposer { } /** - * Checks if the given shader should be rendererd to screen - * @param {number} passIndex position - * @return {boolean} - * @returns {void} - */ + * Checks if the given shader should be rendererd to screen + * @param {number} passIndex position + * @return {boolean} + * @returns {void} + */ private isLastEnabledPass(passIndex: number) { for (let i = passIndex + 1; i < this.passes.length; i++) { if (this.passes[i].enabled) return false; @@ -146,12 +170,12 @@ export class EffectComposer { } /** - * Render the shader-chain for 1 frame - * @public - * @param {number} deltaTime if not given, will calculate its own - * @param {XRFrame} frame Currently rendering XR frame? - * @returns {void} - */ + * Render the shader-chain for 1 frame + * @public + * @param {number} deltaTime if not given, will calculate its own + * @param {XRFrame} frame Currently rendering XR frame? + * @returns {void} + */ public render(deltaTime?: number, frame?: XRFrame) { // deltaTime value is in seconds const dn = performance.now(); @@ -193,13 +217,17 @@ export class EffectComposer { // position const varPos = view.transform.position; - this.xrCam.position.set(camPos.x + varPos.x, + this.xrCam.position.set( + camPos.x + varPos.x, camPos.y + (varPos.y - 1.6), - camPos.z + varPos.z); + camPos.z + varPos.z + ); // orientation const vo = view.transform.orientation; - this.xrCam.setRotationFromQuaternion(new Quaternion(vo.x, vo.y, vo.z, vo.w)); + this.xrCam.setRotationFromQuaternion( + new Quaternion(vo.x, vo.y, vo.z, vo.w) + ); // matrix this.xrCam.projectionMatrix.fromArray(view.projectionMatrix); @@ -213,11 +241,23 @@ export class EffectComposer { this.renderer.setViewport(offX, 0, viewSize, size.height); // pass buffers flipped to avoid swap - this.xrPass.render(this.renderer, this.readBuffer, this.writeBuffer, false, !hasTargets); + this.xrPass.render( + this.renderer, + this.readBuffer, + this.writeBuffer, + false, + !hasTargets + ); this.passes.forEach((pass, i) => { if (!pass.enabled) return; pass.setSize(viewSize, size.height); - pass.render(this.renderer, this.writeBuffer, this.readBuffer, false, this.isLastEnabledPass(i)); + pass.render( + this.renderer, + this.writeBuffer, + this.readBuffer, + false, + this.isLastEnabledPass(i) + ); if (pass.needsSwap) this.swapBuffers(); }); } @@ -231,11 +271,23 @@ export class EffectComposer { this.renderer.setScissor(0, 0, size.width, size.height); this.renderer.setViewport(0, 0, size.width, size.height); // pass buffers flipped to avoid swap - this.normPass.render(this.renderer, this.readBuffer, this.writeBuffer, false, !hasTargets); + this.normPass.render( + this.renderer, + this.readBuffer, + this.writeBuffer, + false, + !hasTargets + ); this.passes.forEach((pass, i) => { if (!pass.enabled) return; pass.setSize(size.width, size.height); - pass.render(this.renderer, this.writeBuffer, this.readBuffer, false, this.isLastEnabledPass(i)); + pass.render( + this.renderer, + this.writeBuffer, + this.readBuffer, + false, + this.isLastEnabledPass(i) + ); if (pass.needsSwap) this.swapBuffers(); }); } @@ -244,15 +296,15 @@ export class EffectComposer { } /** - * Destroy the current shader-chain - * @public - * @param {WebGLRenderTarget} renderTarget target to Reset (optional) - * @returns {void} - */ + * Destroy the current shader-chain + * @public + * @param {WebGLRenderTarget} renderTarget target to Reset (optional) + * @returns {void} + */ public reset(renderTarget?: WebGLRenderTarget) { if (renderTarget === undefined) { renderTarget = this.defaultTarget.clone(); - renderTarget.texture.name = 'EffectComposer.wt'; + renderTarget.texture.name = "EffectComposer.wt"; } this.renderWrite.dispose(); @@ -262,7 +314,7 @@ export class EffectComposer { this.writeBuffer = this.renderWrite; this.renderRead = renderTarget.clone(); - this.renderRead.texture.name = 'EffectComposer.rt'; + this.renderRead.texture.name = "EffectComposer.rt"; this.readBuffer = this.renderRead; this.passes = []; @@ -271,12 +323,12 @@ export class EffectComposer { } /** - * Updated buffer size - * @public - * @param {number} width X - * @param {number} height Y - * @returns {void} - */ + * Updated buffer size + * @public + * @param {number} width X + * @param {number} height Y + * @returns {void} + */ public setSize(width: number, height: number) { this.renderWrite.setSize(width, height); this.renderRead.setSize(width, height); @@ -287,12 +339,12 @@ export class EffectComposer { /* UTILS */ /** - * Some shaders write to Input rather than Output... - * - * This is a workaround to pass their data further down the render-chain - * @ignore - * @returns {void} - */ + * Some shaders write to Input rather than Output... + * + * This is a workaround to pass their data further down the render-chain + * @ignore + * @returns {void} + */ private swapBuffers() { const tmp = this.readBuffer; this.readBuffer = this.writeBuffer; From b5e10c8d144c3b83dc2430bbc28869734c102185 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sun, 6 Mar 2022 15:43:42 +0100 Subject: [PATCH 52/76] Fix: im- & exports --- src/CComponent.ts | 2 +- src/CSettings.ts | 2 +- src/ReloadHelper.ts | 2 +- src/WEICUE.ts | 1 + src/WEWA.ts | 2 +- src/WarnHelper.ts | 2 +- src/XRHelper.ts | 2 +- src/index.ts | 16 +- src/offline/OfflineHelper.ts | 27 ++-- src/three.ts | 2 +- src/three/EffectComposer.ts | 12 +- src/three/pass/BasePass.ts | 27 ++-- src/three/pass/FullScreenHelper.ts | 54 ++++--- src/three/pass/RenderPass.ts | 58 +++---- src/three/pass/ShaderPass.ts | 7 +- src/three/pass/UnrealBloomPass.ts | 162 +++++++++---------- src/three/shader/BlurShader.ts | 39 ++--- src/three/shader/ChromaticShader.ts | 40 ++--- src/three/shader/FXAAShader.ts | 39 +++-- src/three/shader/FractalMirrorShader.ts | 47 +++--- src/three/shader/LuminosityHighPassShader.ts | 49 +++--- src/wasc-worker | 2 +- src/weas/WEAS.ts | 8 +- 23 files changed, 304 insertions(+), 298 deletions(-) diff --git a/src/CComponent.ts b/src/CComponent.ts index 3dbfbc5..63b96d5 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -7,7 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import { CSettings, Smallog } from "./"; +import { CSettings, Smallog } from "."; /** * Base-Component for a TypeScript Web Wallpaper diff --git a/src/CSettings.ts b/src/CSettings.ts index 0ecbde3..acc6e99 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -7,7 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import { Smallog } from "./"; +import { Smallog } from "."; /** * Base-Component Settings helper diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index 4eb63dd..1871fbd 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -11,7 +11,7 @@ * */ -import { CComponent, CSettings, waitReady } from "./"; +import { CComponent, CSettings, waitReady } from "."; /** * Reload-bar settings diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 0028ca3..acc7f52 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -8,6 +8,7 @@ */ import { CComponent, CSettings, rgbToObj, Smallog, waitReady, WEAS } from "."; + import { ICUE } from "./ICUE"; const IMG_SRC = "./img/icue.png"; diff --git a/src/WEWA.ts b/src/WEWA.ts index f2e2d9e..07d6e37 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -7,7 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import { Smallog, waitReady, OfflineHelper, WascUtil } from "./"; +import { Smallog, waitReady, OfflineHelper, WascUtil } from "."; const LogHead = "[WEWWA] "; const DefLang = "de-de"; diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 8662867..c78a705 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -7,7 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import { CSettings, CComponent, waitReady } from "./"; +import { CSettings, CComponent, waitReady } from "."; const ELM_ID = "triggerwarn"; const IMG_SRC = "./img/triggerwarn.png"; diff --git a/src/XRHelper.ts b/src/XRHelper.ts index afdb58e..2cdc3c4 100644 --- a/src/XRHelper.ts +++ b/src/XRHelper.ts @@ -4,7 +4,7 @@ * @author Mugen87 / https://github.com/Mugen87 */ -import { CSettings, CComponent, waitReady } from "./"; +import { CSettings, CComponent, waitReady } from "."; import { XRSessionInit } from "./three.ts/src/XRWebGL"; /** diff --git a/src/index.ts b/src/index.ts index 32ece0a..71cb704 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,23 +5,27 @@ export * from "./offline/OfflineHelper"; export * from "./three"; // three.ts lib -export * from "./three.ts/src"; +//export * from "./three.ts/src"; // Web-AssemblyScript entry-point export * from "./wasc-worker"; +// basic modules +export * from "./CSettings"; +export * from "./CComponent"; + // audio processing export * from "./weas"; +// led / icue +export * from "./WEICUE"; + // single modules -export * from "./CComponent"; -export * from "./CSettings"; +export * from "./Util"; export * from "./FPSta"; export * from "./LoadHelper"; export * from "./ReloadHelper"; export * from "./Smallog"; -export * from "./Util"; export * from "./WarnHelper"; -export * from "./WEICUE"; -export * from "./WEWA"; export * from "./XRHelper"; +export * from "./WEWA"; diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index 21d31a1..8677a06 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -7,7 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import { Smallog } from "../"; +import { Smallog } from ".."; // this should pack the serviceworker like a webwoker. import OfflineWorker from "worker-loader!./Offline"; @@ -63,16 +63,9 @@ function register( navigator.serviceWorker .register(workerPath, { scope: "/" }) .then( - () => - Smallog.info( - "service-worker registration complete.", - oh - ), + () => Smallog.info("service-worker registration complete.", oh), (reason) => - Smallog.error( - "service-worker registration failur: " + reason, - oh - ) + Smallog.error("service-worker registration failur: " + reason, oh) ) .then(() => resolve(true)); return true; @@ -91,14 +84,12 @@ function register( async function reset(): Promise { return new Promise((resolve) => { if ("serviceWorker" in navigator) { - navigator.serviceWorker - .getRegistrations() - .then(async (registrations) => { - for (const registration of registrations) { - await registration.unregister(); - } - resolve(true); - }); + navigator.serviceWorker.getRegistrations().then(async (registrations) => { + for (const registration of registrations) { + await registration.unregister(); + } + resolve(true); + }); } else { Smallog.error("not supported!", oh); resolve(false); diff --git a/src/three.ts b/src/three.ts index 288c8c3..7be17e4 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 288c8c3d8bff62ec2f6d3c0b3956aea730597ecc +Subproject commit 7be17e4a7a8dcb992027ca39e0e5e9a4033d3e26 diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts index 56198e6..964a854 100644 --- a/src/three/EffectComposer.ts +++ b/src/three/EffectComposer.ts @@ -4,18 +4,18 @@ * @author hexxone / https://hexx.one */ +import { RenderPass, BasePass } from ".."; + import { - RenderPass, - BasePass, LinearFilter, + PerspectiveCamera, + Quaternion, RGBAFormat, Scene, - PerspectiveCamera, - WebGLRenderer, Vector2, + WebGLRenderer, WebGLRenderTarget, - Quaternion, -} from "../"; +} from "../three.ts/src"; /** * render shader chain diff --git a/src/three/pass/BasePass.ts b/src/three/pass/BasePass.ts index 90d60eb..38a2f3c 100644 --- a/src/three/pass/BasePass.ts +++ b/src/three/pass/BasePass.ts @@ -1,12 +1,13 @@ -import {WebGLRenderer, WebGLRenderTarget} from '../../'; - /** -* @author alteredq / http://alteredqualia.com/ -* @author hexxone / https://hexx.one -* -* Basic shader pass interface -*/ -export type BasePass ={ + * @author alteredq / http://alteredqualia.com/ + * @author hexxone / https://hexx.one + * + * Basic shader pass interface + */ + +import { WebGLRenderer, WebGLRenderTarget } from "../../three.ts/src"; + +export type BasePass = { // child name name: string; @@ -26,5 +27,11 @@ export type BasePass ={ setSize(width: number, height: number); - render(renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, maskActive: boolean, renderToScreen: boolean); -} + render( + renderer: WebGLRenderer, + writeBuffer: WebGLRenderTarget, + readBuffer: WebGLRenderTarget, + maskActive: boolean, + renderToScreen: boolean + ); +}; diff --git a/src/three/pass/FullScreenHelper.ts b/src/three/pass/FullScreenHelper.ts index 3351b16..b54095a 100644 --- a/src/three/pass/FullScreenHelper.ts +++ b/src/three/pass/FullScreenHelper.ts @@ -1,16 +1,24 @@ /** -* @author alteredq / http://alteredqualia.com/ -* -* @author hexxone / https://hexx.one -*/ + * @author alteredq / http://alteredqualia.com/ + * + * @author hexxone / https://hexx.one + */ -import {Camera, BufferGeometry, Mesh, Material, OrthographicCamera, PlaneBufferGeometry, WebGLRenderer} from '../../'; +import { + Camera, + BufferGeometry, + Mesh, + Material, + OrthographicCamera, + PlaneBufferGeometry, + WebGLRenderer, +} from "../../three.ts/src"; /** -* Helper for passes that need to fill the viewport with a single quad. -* used to render on a PlaneGeometry ("texture") -* @public -*/ + * Helper for passes that need to fill the viewport with a single quad. + * used to render on a PlaneGeometry ("texture") + * @public + */ export class FullScreenHelper { private _mat = null; @@ -19,9 +27,9 @@ export class FullScreenHelper { public mesh: Mesh = null; /** - * instantiate - * @param {Material} material - */ + * instantiate + * @param {Material} material + */ constructor(material: Material) { this._mat = material; this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1); @@ -30,32 +38,32 @@ export class FullScreenHelper { } /** - * precompile shader - * @param {WebGLRenderer} renderer - */ + * precompile shader + * @param {WebGLRenderer} renderer + */ public prepare(renderer: WebGLRenderer) { renderer.compile(this.mesh as any, this.camera); } /** - * Change mesh material - * @param {Material} mat - */ + * Change mesh material + * @param {Material} mat + */ public setMaterial(mat: Material) { this.mesh.material = mat; } /** - * Render the 2D-environment - * @param {WebGLRenderer} renderer - */ + * Render the 2D-environment + * @param {WebGLRenderer} renderer + */ public render(renderer: WebGLRenderer) { renderer.render(this.mesh, this.camera); } /** - * Destroy 2D-environment - */ + * Destroy 2D-environment + */ public dispose() { this.camera.clear(); this.mesh.clear(); diff --git a/src/three/pass/RenderPass.ts b/src/three/pass/RenderPass.ts index 149b2ed..268327f 100644 --- a/src/three/pass/RenderPass.ts +++ b/src/three/pass/RenderPass.ts @@ -4,6 +4,8 @@ * @author hexxone / https://hexx.one */ +import { BasePass } from "../.."; + import { Camera, Color, @@ -11,8 +13,7 @@ import { Scene, WebGLRenderer, WebGLRenderTarget, -} from "../../"; -import { BasePass } from "./BasePass"; +} from "../../three.ts/src"; /** * Shader Render Helper @@ -34,13 +35,13 @@ export class RenderPass implements BasePass { overMat: Material = null; /** - * Construct helper - * @param {Scene} scene Scene - * @param {Camera} camera Camera - * @param {Material} overMat Override material - * @param {Color} clearColor Clear color - * @param {number} clearAlpha Clear alpha - */ + * Construct helper + * @param {Scene} scene Scene + * @param {Camera} camera Camera + * @param {Material} overMat Override material + * @param {Color} clearColor Clear color + * @param {number} clearAlpha Clear alpha + */ constructor( scene: Scene, camera: Camera, @@ -58,39 +59,39 @@ export class RenderPass implements BasePass { } /** - * precompile shader - * @param {WebGLRenderer} renderer Context - */ + * precompile shader + * @param {WebGLRenderer} renderer Context + */ public prepare(renderer: WebGLRenderer) { renderer.compile(this.scene, this.camera); } /** - * Destroy shader - */ + * Destroy shader + */ public dispose() { throw new Error("Method not implemented."); } /** - * Updated screen size - * @param {number} width X - * @param {number} height Y - */ + * Updated screen size + * @param {number} width X + * @param {number} height Y + */ public setSize(width: number, height: number) { return; } /** - * Render Frame - * @param {WebGLRenderer} renderer Context - * @param {WebGLRenderTarget} writeBuffer Output - * @param {WebGLRenderTarget} readBuffer Input - * @param {boolean} maskActive filter - * @param {boolean} renderToScreen render to canvas OR buffer - * @param {Camera} camera (optional) - * @public - */ + * Render Frame + * @param {WebGLRenderer} renderer Context + * @param {WebGLRenderTarget} writeBuffer Output + * @param {WebGLRenderTarget} readBuffer Input + * @param {boolean} maskActive filter + * @param {boolean} renderToScreen render to canvas OR buffer + * @param {Camera} camera (optional) + * @public + */ public render( renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, @@ -128,8 +129,7 @@ export class RenderPass implements BasePass { renderer.render(this.scene, this.camera); - if (this.clearColor) - renderer.setClearColor(oldClearColor, oldClearAlpha); + if (this.clearColor) renderer.setClearColor(oldClearColor, oldClearAlpha); this.scene.overrideMaterial = null; renderer.autoClear = oldAutoClear; diff --git a/src/three/pass/ShaderPass.ts b/src/three/pass/ShaderPass.ts index c2cccf7..4e0a867 100644 --- a/src/three/pass/ShaderPass.ts +++ b/src/three/pass/ShaderPass.ts @@ -4,16 +4,15 @@ * @author hexxone / https://hexx.one */ +import { BasePass, BaseShader, FullScreenHelper } from "../.."; + import { - BasePass, - BaseShader, - FullScreenHelper, ShaderMaterial, UniformsUtils, Vector2, WebGLRenderer, WebGLRenderTarget, -} from "../../"; +} from "../../three.ts/src"; /** * ThreeJS Pass for easy full screen shaders diff --git a/src/three/pass/UnrealBloomPass.ts b/src/three/pass/UnrealBloomPass.ts index 6219e3a..de64916 100644 --- a/src/three/pass/UnrealBloomPass.ts +++ b/src/three/pass/UnrealBloomPass.ts @@ -3,13 +3,16 @@ */ import { - AdditiveBlending, BasePass, - Color, CopyShader, FullScreenHelper, - LinearFilter, LuminosityHighPassShader, +} from "../.."; + +import { + AdditiveBlending, + Color, + LinearFilter, MeshBasicMaterial, RGBAFormat, ShaderMaterial, @@ -18,7 +21,7 @@ import { Vector3, WebGLRenderer, WebGLRenderTarget, -} from "../../"; +} from "../../three.ts/src"; /** * Inspired from Unreal Engine @@ -61,12 +64,12 @@ export class UnrealBloomPass implements BasePass { private blurDirY = new Vector2(0.0, 1.0); /** - * Construct bloom shader - * @param {Vector2} resolution size - * @param {number} strength multiplier - * @param {number} radius size - * @param {number} threshold min val - */ + * Construct bloom shader + * @param {Vector2} resolution size + * @param {number} strength multiplier + * @param {number} radius size + * @param {number} threshold min val + */ constructor( resolution: Vector2, strength: number, @@ -92,20 +95,12 @@ export class UnrealBloomPass implements BasePass { this.renderTargetBright.texture.generateMipmaps = false; for (let i = 0; i < this.nMips; i++) { - const renderTargetHorizonal = new WebGLRenderTarget( - resx, - resy, - pars - ); + const renderTargetHorizonal = new WebGLRenderTarget(resx, resy, pars); renderTargetHorizonal.texture.name = "UnrealBloomPass.h" + i; renderTargetHorizonal.texture.generateMipmaps = false; this.renderTargetsHorizontal.push(renderTargetHorizonal); - const renderTargetVertical = new WebGLRenderTarget( - resx, - resy, - pars - ); + const renderTargetVertical = new WebGLRenderTarget(resx, resy, pars); renderTargetVertical.texture.name = "UnrealBloomPass.v" + i; renderTargetVertical.texture.generateMipmaps = false; this.renderTargetsVertical.push(renderTargetVertical); @@ -124,8 +119,7 @@ export class UnrealBloomPass implements BasePass { this.materialHighPassFilter = new ShaderMaterial(); this.materialHighPassFilter.uniforms = this.highPassUniforms; this.materialHighPassFilter.vertexShader = highPassShader.vertexShader; - this.materialHighPassFilter.fragmentShader = - highPassShader.fragmentShader; + this.materialHighPassFilter.fragmentShader = highPassShader.fragmentShader; this.materialHighPassFilter.defines = {}; // Gaussian Blur Materials @@ -137,8 +131,10 @@ export class UnrealBloomPass implements BasePass { this.separableBlurMaterials.push( this.getSeperableBlurMaterial(kernelSizeArray[i]) ); - this.separableBlurMaterials[i].uniforms["texSize"].value = - new Vector2(resx, resy); + this.separableBlurMaterials[i].uniforms["texSize"].value = new Vector2( + resx, + resy + ); resx = Math.round(resx / 2); resy = Math.round(resy / 2); @@ -147,15 +143,15 @@ export class UnrealBloomPass implements BasePass { // Composite material this.compositeMaterial = this.getCompositeMaterial(this.nMips); this.compositeMaterial.uniforms["blurTexture1"].value = - this.renderTargetsVertical[0].texture; + this.renderTargetsVertical[0].texture; this.compositeMaterial.uniforms["blurTexture2"].value = - this.renderTargetsVertical[1].texture; + this.renderTargetsVertical[1].texture; this.compositeMaterial.uniforms["blurTexture3"].value = - this.renderTargetsVertical[2].texture; + this.renderTargetsVertical[2].texture; this.compositeMaterial.uniforms["blurTexture4"].value = - this.renderTargetsVertical[3].texture; + this.renderTargetsVertical[3].texture; this.compositeMaterial.uniforms["blurTexture5"].value = - this.renderTargetsVertical[4].texture; + this.renderTargetsVertical[4].texture; this.compositeMaterial.uniforms["bloomStrength"].value = strength; this.compositeMaterial.uniforms["bloomRadius"].value = 0.1; this.compositeMaterial.needsUpdate = true; @@ -170,7 +166,7 @@ export class UnrealBloomPass implements BasePass { new Vector3(1, 1, 1), ]; this.compositeMaterial.uniforms["bloomTintColors"].value = - this.bloomTintColors; + this.bloomTintColors; // copy material @@ -190,18 +186,18 @@ export class UnrealBloomPass implements BasePass { } /** - * precompile shader - * @param {WebGLRenderer} renderer renderer - * @returns {void} - */ + * precompile shader + * @param {WebGLRenderer} renderer renderer + * @returns {void} + */ prepare(renderer: WebGLRenderer) { this.fsQuad.prepare(renderer); } /** - * Destroy shader - * @returns {void} - */ + * Destroy shader + * @returns {void} + */ dispose() { for (let i = 0; i < this.renderTargetsHorizontal.length; i++) { this.renderTargetsHorizontal[i].dispose(); @@ -214,11 +210,11 @@ export class UnrealBloomPass implements BasePass { } /** - * Updated screen size - * @param {number} width X - * @param {number} height Y - * @returns {void} - */ + * Updated screen size + * @param {number} width X + * @param {number} height Y + * @returns {void} + */ setSize(width: number, height: number) { let resx = Math.round(width / 2); let resy = Math.round(height / 2); @@ -228,8 +224,10 @@ export class UnrealBloomPass implements BasePass { this.renderTargetsHorizontal[i].setSize(resx, resy); this.renderTargetsVertical[i].setSize(resx, resy); - this.separableBlurMaterials[i].uniforms["texSize"].value = - new Vector2(resx, resy); + this.separableBlurMaterials[i].uniforms["texSize"].value = new Vector2( + resx, + resy + ); resx = Math.round(resx / 2); resy = Math.round(resy / 2); @@ -237,14 +235,14 @@ export class UnrealBloomPass implements BasePass { } /** - * Render Frame - * @param {WebGLRenderer} renderer Context - * @param {WebGLRenderTarget} writeBuffer Output - * @param {WebGLRenderTarget} readBuffer Input - * @param {boolean} maskActive filter - * @param {boolean} renderToScreen render to canvas OR buffer - * @returns {void} - */ + * Render Frame + * @param {WebGLRenderer} renderer Context + * @param {WebGLRenderTarget} writeBuffer Output + * @param {WebGLRenderTarget} readBuffer Input + * @param {boolean} maskActive filter + * @param {boolean} renderToScreen render to canvas OR buffer + * @returns {void} + */ render( renderer: WebGLRenderer, writeBuffer: WebGLRenderTarget, @@ -288,17 +286,17 @@ export class UnrealBloomPass implements BasePass { this.fsQuad.setMaterial(this.separableBlurMaterials[i]); this.separableBlurMaterials[i].uniforms["colorTexture"].value = - inputRenderTarget.texture; + inputRenderTarget.texture; this.separableBlurMaterials[i].uniforms["direction"].value = - this.blurDirX; + this.blurDirX; renderer.setRenderTarget(this.renderTargetsHorizontal[i]); renderer.clear(); this.fsQuad.render(renderer); this.separableBlurMaterials[i].uniforms["colorTexture"].value = - this.renderTargetsHorizontal[i].texture; + this.renderTargetsHorizontal[i].texture; this.separableBlurMaterials[i].uniforms["direction"].value = - this.blurDirY; + this.blurDirY; renderer.setRenderTarget(this.renderTargetsVertical[i]); renderer.clear(); this.fsQuad.render(renderer); @@ -312,7 +310,7 @@ export class UnrealBloomPass implements BasePass { this.compositeMaterial.uniforms["bloomStrength"].value = this.strength; this.compositeMaterial.uniforms["bloomRadius"].value = this.radius; this.compositeMaterial.uniforms["bloomTintColors"].value = - this.bloomTintColors; + this.bloomTintColors; renderer.setRenderTarget(this.renderTargetsHorizontal[0]); renderer.clear(); @@ -322,7 +320,7 @@ export class UnrealBloomPass implements BasePass { this.fsQuad.setMaterial(this.materialCopy); this.copyUniforms["tDiffuse"].value = - this.renderTargetsHorizontal[0].texture; + this.renderTargetsHorizontal[0].texture; if (maskActive) renderer.context.enable(renderer.context.STENCIL_TEST); @@ -335,22 +333,22 @@ export class UnrealBloomPass implements BasePass { } /** - * Make seperable material - * @param {number} kernelRadius size - * @return {ShaderMaterial} material - */ + * Make seperable material + * @param {number} kernelRadius size + * @return {ShaderMaterial} material + */ private getSeperableBlurMaterial(kernelRadius) { const sm = new ShaderMaterial(); sm.defines = { - "KERNEL_RADIUS": kernelRadius, - "SIGMA": kernelRadius, + KERNEL_RADIUS: kernelRadius, + SIGMA: kernelRadius, }; sm.uniforms = { - "colorTexture": { value: null }, - "texSize": { value: new Vector2(0.5, 0.5) }, - "direction": { value: new Vector2(0.5, 0.5) }, + colorTexture: { value: null }, + texSize: { value: new Vector2(0.5, 0.5) }, + direction: { value: new Vector2(0.5, 0.5) }, }; sm.vertexShader = ` @@ -393,27 +391,27 @@ export class UnrealBloomPass implements BasePass { } /** - * Make helper material - * @param {number} nMips MipMaps - * @return {ShaderMaterial} material - */ + * Make helper material + * @param {number} nMips MipMaps + * @return {ShaderMaterial} material + */ private getCompositeMaterial(nMips) { const sm = new ShaderMaterial(); sm.defines = { - "NUM_MIPS": nMips, + NUM_MIPS: nMips, }; sm.uniforms = { - "blurTexture1": { value: null }, - "blurTexture2": { value: null }, - "blurTexture3": { value: null }, - "blurTexture4": { value: null }, - "blurTexture5": { value: null }, - "dirtTexture": { value: null }, - "bloomStrength": { value: 1.0 }, - "bloomFactors": { value: null }, - "bloomTintColors": { value: null }, - "bloomRadius": { value: 0.0 }, + blurTexture1: { value: null }, + blurTexture2: { value: null }, + blurTexture3: { value: null }, + blurTexture4: { value: null }, + blurTexture5: { value: null }, + dirtTexture: { value: null }, + bloomStrength: { value: 1.0 }, + bloomFactors: { value: null }, + bloomTintColors: { value: null }, + bloomRadius: { value: 0.0 }, }; sm.vertexShader = ` diff --git a/src/three/shader/BlurShader.ts b/src/three/shader/BlurShader.ts index 3c628e4..de48ce4 100644 --- a/src/three/shader/BlurShader.ts +++ b/src/three/shader/BlurShader.ts @@ -1,32 +1,33 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ -import {BaseShader, Vector2} from '../..'; +import { BaseShader } from "../.."; +import { Vector2 } from "../../three.ts/src"; -import vertex from './vertex/Basic.glsl'; -import fragment from './fragment/Blur.glsl'; +import vertex from "./vertex/Basic.glsl"; +import fragment from "./fragment/Blur.glsl"; /** -* Blur shader with Alpha support -* @public -* @implements {BaseShader} -*/ + * Blur shader with Alpha support + * @public + * @implements {BaseShader} + */ export class BlurShader implements BaseShader { defines = null; - shaderID = 'blurShader'; + shaderID = "blurShader"; uniforms = { - "tDiffuse": {value: null}, - "iResolution": {value: new Vector2(1, 1)}, - "u_sigma": {value: 0.5}, - "u_dir": {value: new Vector2(0.1, 0.1)}, + tDiffuse: { value: null }, + iResolution: { value: new Vector2(1, 1) }, + u_sigma: { value: 0.5 }, + u_dir: { value: new Vector2(0.1, 0.1) }, }; vertexShader = vertex; diff --git a/src/three/shader/ChromaticShader.ts b/src/three/shader/ChromaticShader.ts index 0d4e59d..4900f76 100644 --- a/src/three/shader/ChromaticShader.ts +++ b/src/three/shader/ChromaticShader.ts @@ -1,35 +1,35 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ -import {Vector2} from '../../'; -import {BaseShader} from './BaseShader'; +import { BaseShader } from "./BaseShader"; +import { Vector2 } from "../../three.ts/src"; -import vertex from './vertex/Basic.glsl'; -import fragment from './fragment/Chromatic.glsl'; +import vertex from "./vertex/Basic.glsl"; +import fragment from "./fragment/Chromatic.glsl"; /** -* Chromatic Abberation shader with alpha support -* @public -* @implements {BaseShader} -*/ + * Chromatic Abberation shader with alpha support + * @public + * @implements {BaseShader} + */ export class ChromaticShader implements BaseShader { defines = null; - shaderID = 'chromaticShader'; + shaderID = "chromaticShader"; uniforms = { - "tDiffuse": {value: null}, - "iResolution": {value: new Vector2(1, 1)}, - "strength": {value: 10.0}, + tDiffuse: { value: null }, + iResolution: { value: new Vector2(1, 1) }, + strength: { value: 10.0 }, }; vertexShader = vertex; fragmentShader = fragment; -}; +} diff --git a/src/three/shader/FXAAShader.ts b/src/three/shader/FXAAShader.ts index 419aabc..3cd5aee 100644 --- a/src/three/shader/FXAAShader.ts +++ b/src/three/shader/FXAAShader.ts @@ -1,36 +1,35 @@ /** -* @author alteredq / http://alteredqualia.com/ -* @author davidedc / http://www.sketchpatch.net/ -* @author hexxone / https://hexx.one -*/ + * @author alteredq / http://alteredqualia.com/ + * @author davidedc / http://www.sketchpatch.net/ + * @author hexxone / https://hexx.one + */ -import {Vector2, BaseShader} from '../../'; +import { BaseShader } from "../.."; +import { Vector2 } from "../../three.ts/src"; -import vertex from './vertex/Basic.glsl'; -import fragment from './fragment/FXAA.glsl'; +import vertex from "./vertex/Basic.glsl"; +import fragment from "./fragment/FXAA.glsl"; /** -* NVIDIA FXAA by Timothy Lottes -* http://timothylottes.blogspot.com/2011/06/fxaa3-source-released.html -* - WebGL port by @supereggbert -* http://www.glge.org/demos/fxaa/ -* -* @public -* @implements {BaseShader} -*/ + * NVIDIA FXAA by Timothy Lottes + * http://timothylottes.blogspot.com/2011/06/fxaa3-source-released.html + * - WebGL port by @supereggbert + * http://www.glge.org/demos/fxaa/ + * + * @public + * @implements {BaseShader} + */ export class FXAAShader implements BaseShader { defines = null; - shaderID = 'fxaaShader'; + shaderID = "fxaaShader"; uniforms = { - "tDiffuse": {value: null}, - "resolution": {value: new Vector2(1 / 1024, 1 / 512)}, + tDiffuse: { value: null }, + resolution: { value: new Vector2(1 / 1024, 1 / 512) }, }; vertexShader = vertex; fragmentShader = fragment; } - - diff --git a/src/three/shader/FractalMirrorShader.ts b/src/three/shader/FractalMirrorShader.ts index db5e2b9..0fdb9d6 100644 --- a/src/three/shader/FractalMirrorShader.ts +++ b/src/three/shader/FractalMirrorShader.ts @@ -1,36 +1,37 @@ /** -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -* -* @description -*/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + * @description + */ -import {Vector2, BaseShader} from '../../'; +import { BaseShader } from "../.."; +import { Vector2 } from "../../three.ts/src"; -import vertex from './vertex/Basic.glsl'; -import fragment from './fragment/FractalMirror.glsl'; +import vertex from "./vertex/Basic.glsl"; +import fragment from "./fragment/FractalMirror.glsl"; /** -* Customized Kaleidoscope shader -* Inspired by ackleyrc: https://www.shadertoy.com/view/llXcRl -* -* @public -* @implements {BaseShader} -*/ + * Customized Kaleidoscope shader + * Inspired by ackleyrc: https://www.shadertoy.com/view/llXcRl + * + * @public + * @implements {BaseShader} + */ export class FractalMirrorShader implements BaseShader { defines = null; - shaderID = 'fractalMirror'; + shaderID = "fractalMirror"; uniforms = { - "tDiffuse": {value: null}, - "iResolution": {value: new Vector2(16, 9)}, - "numSides": {value: 2.0}, // minimum value - "invert": {value: false}, + tDiffuse: { value: null }, + iResolution: { value: new Vector2(16, 9) }, + numSides: { value: 2.0 }, // minimum value + invert: { value: false }, }; vertexShader = vertex; diff --git a/src/three/shader/LuminosityHighPassShader.ts b/src/three/shader/LuminosityHighPassShader.ts index 8c42f57..b461fcd 100644 --- a/src/three/shader/LuminosityHighPassShader.ts +++ b/src/three/shader/LuminosityHighPassShader.ts @@ -1,39 +1,40 @@ /** -* @author bhouston / http://clara.io/ -* @author hexxone / https://hexx.one -* -* @license -* Copyright (c) 2021 hexxone All rights reserved. -* Licensed under the GNU GENERAL PUBLIC LICENSE. -* See LICENSE file in the project root for full license information. -*/ + * @author bhouston / http://clara.io/ + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ -import {Color, BaseShader} from '../../'; +import { BaseShader } from "../.."; +import { Color } from "../../three.ts/src"; -import vertex from './vertex/Basic.glsl'; -import fragment from './fragment/Luminosity.glsl'; +import vertex from "./vertex/Basic.glsl"; +import fragment from "./fragment/Luminosity.glsl"; /** -* Luminosity -* http://en.wikipedia.org/wiki/Luminosity -* -* @public -* @implements {BaseShader} -*/ + * Luminosity + * http://en.wikipedia.org/wiki/Luminosity + * + * @public + * @implements {BaseShader} + */ export class LuminosityHighPassShader implements BaseShader { defines = null; - shaderID = 'luminosityHighPass'; + shaderID = "luminosityHighPass"; uniforms = { - "tDiffuse": {value: null}, - "luminosityThreshold": {value: 1.0}, - "smoothWidth": {value: 1.0}, - "defaultColor": {value: new Color(0x000000)}, // @TODO might need to set to BG color? - "defaultOpacity": {value: 0.0}, + tDiffuse: { value: null }, + luminosityThreshold: { value: 1.0 }, + smoothWidth: { value: 1.0 }, + defaultColor: { value: new Color(0x000000) }, // @TODO might need to set to BG color? + defaultOpacity: { value: 0.0 }, }; vertexShader = vertex; fragmentShader = fragment; -}; +} diff --git a/src/wasc-worker b/src/wasc-worker index ddf2273..bb5674c 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit ddf2273333ca5a104c8aa1cde8fefd37a6ccfeab +Subproject commit bb5674cb163a20c8c8f0ba2a17853d757f9086e5 diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 6e7fa15..67269dc 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -13,13 +13,9 @@ import { Smallog, waitReady, WascInterface, - wascWorker, - WascLoader, - ASUtil, sharedWorker, -} from "../"; - -import { Bea_ts } from "./Bea"; + Bea_ts, +} from ".."; const DAT_LEN = 128; const LISTENAME = "wallpaperRegisterAudioListener"; From 7280184e024f43887fccfe60f6e223f6a4f05041 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 24 Sep 2022 14:13:50 +0200 Subject: [PATCH 53/76] feat: working compile --- src/XRHelper.ts | 2 +- src/index.ts | 3 -- src/offline/{Offline.ts => Offline.worker.ts} | 0 src/offline/OfflineHelper.ts | 6 ++- src/three.ts | 2 +- src/three/EffectComposer.ts | 2 +- src/three/pass/BasePass.ts | 2 +- src/three/pass/FullScreenHelper.ts | 2 +- src/three/pass/RenderPass.ts | 2 +- src/three/pass/ShaderPass.ts | 2 +- src/three/pass/UnrealBloomPass.ts | 2 +- src/three/shader/BlurShader.ts | 2 +- src/three/shader/ChromaticShader.ts | 2 +- src/three/shader/FXAAShader.ts | 2 +- src/three/shader/FractalMirrorShader.ts | 2 +- src/three/shader/LuminosityHighPassShader.ts | 2 +- src/three/shader/loader/index.js | 25 +++++++----- src/wasc-worker | 2 +- src/worker-loader.d.ts | 29 -------------- src/worker-loader.d.txt | 40 +++++++++++++++++++ 20 files changed, 74 insertions(+), 57 deletions(-) rename src/offline/{Offline.ts => Offline.worker.ts} (100%) delete mode 100644 src/worker-loader.d.ts create mode 100644 src/worker-loader.d.txt diff --git a/src/XRHelper.ts b/src/XRHelper.ts index 2cdc3c4..cbacdff 100644 --- a/src/XRHelper.ts +++ b/src/XRHelper.ts @@ -5,7 +5,7 @@ */ import { CSettings, CComponent, waitReady } from "."; -import { XRSessionInit } from "./three.ts/src/XRWebGL"; +import { XRSessionInit } from "three.ts/src/XRWebGL"; /** * XR Settings diff --git a/src/index.ts b/src/index.ts index 71cb704..a509a26 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,9 +4,6 @@ export * from "./offline/OfflineHelper"; // custom effects export * from "./three"; -// three.ts lib -//export * from "./three.ts/src"; - // Web-AssemblyScript entry-point export * from "./wasc-worker"; diff --git a/src/offline/Offline.ts b/src/offline/Offline.worker.ts similarity index 100% rename from src/offline/Offline.ts rename to src/offline/Offline.worker.ts diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index 8677a06..3c2d418 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -10,7 +10,9 @@ import { Smallog } from ".."; // this should pack the serviceworker like a webwoker. -import OfflineWorker from "worker-loader!./Offline"; + +// import OfflineWorker from "worker-loader!./Offline.worker"; +const OfflineWorker = () => new Worker(new URL("./Offline.worker.js", import.meta.url)); const oh = "[OfflineHelper] "; @@ -34,7 +36,7 @@ const oh = "[OfflineHelper] "; // eslint-disable-next-line require-jsdoc // eslint-disable-next-line @typescript-eslint/no-unused-vars function DontRemove() { - return new OfflineWorker(); + return OfflineWorker(); } /** diff --git a/src/three.ts b/src/three.ts index 7be17e4..69a3c35 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 7be17e4a7a8dcb992027ca39e0e5e9a4033d3e26 +Subproject commit 69a3c35b945e670a633a6f485f115dd9c32e977e diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts index 964a854..c8e7d5d 100644 --- a/src/three/EffectComposer.ts +++ b/src/three/EffectComposer.ts @@ -15,7 +15,7 @@ import { Vector2, WebGLRenderer, WebGLRenderTarget, -} from "../three.ts/src"; +} from "three.ts/src/"; /** * render shader chain diff --git a/src/three/pass/BasePass.ts b/src/three/pass/BasePass.ts index 38a2f3c..cb1878e 100644 --- a/src/three/pass/BasePass.ts +++ b/src/three/pass/BasePass.ts @@ -5,7 +5,7 @@ * Basic shader pass interface */ -import { WebGLRenderer, WebGLRenderTarget } from "../../three.ts/src"; +import { WebGLRenderer, WebGLRenderTarget } from "three.ts/src/"; export type BasePass = { // child name diff --git a/src/three/pass/FullScreenHelper.ts b/src/three/pass/FullScreenHelper.ts index b54095a..23ef971 100644 --- a/src/three/pass/FullScreenHelper.ts +++ b/src/three/pass/FullScreenHelper.ts @@ -12,7 +12,7 @@ import { OrthographicCamera, PlaneBufferGeometry, WebGLRenderer, -} from "../../three.ts/src"; +} from "three.ts/src/"; /** * Helper for passes that need to fill the viewport with a single quad. diff --git a/src/three/pass/RenderPass.ts b/src/three/pass/RenderPass.ts index 268327f..e05cf32 100644 --- a/src/three/pass/RenderPass.ts +++ b/src/three/pass/RenderPass.ts @@ -13,7 +13,7 @@ import { Scene, WebGLRenderer, WebGLRenderTarget, -} from "../../three.ts/src"; +} from "three.ts/src/"; /** * Shader Render Helper diff --git a/src/three/pass/ShaderPass.ts b/src/three/pass/ShaderPass.ts index 4e0a867..f0acf37 100644 --- a/src/three/pass/ShaderPass.ts +++ b/src/three/pass/ShaderPass.ts @@ -12,7 +12,7 @@ import { Vector2, WebGLRenderer, WebGLRenderTarget, -} from "../../three.ts/src"; +} from "three.ts/src/"; /** * ThreeJS Pass for easy full screen shaders diff --git a/src/three/pass/UnrealBloomPass.ts b/src/three/pass/UnrealBloomPass.ts index de64916..53162ec 100644 --- a/src/three/pass/UnrealBloomPass.ts +++ b/src/three/pass/UnrealBloomPass.ts @@ -21,7 +21,7 @@ import { Vector3, WebGLRenderer, WebGLRenderTarget, -} from "../../three.ts/src"; +} from "three.ts/src/"; /** * Inspired from Unreal Engine diff --git a/src/three/shader/BlurShader.ts b/src/three/shader/BlurShader.ts index de48ce4..4137ba7 100644 --- a/src/three/shader/BlurShader.ts +++ b/src/three/shader/BlurShader.ts @@ -8,7 +8,7 @@ */ import { BaseShader } from "../.."; -import { Vector2 } from "../../three.ts/src"; +import { Vector2 } from "three.ts/src/"; import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/Blur.glsl"; diff --git a/src/three/shader/ChromaticShader.ts b/src/three/shader/ChromaticShader.ts index 4900f76..5673e23 100644 --- a/src/three/shader/ChromaticShader.ts +++ b/src/three/shader/ChromaticShader.ts @@ -8,7 +8,7 @@ */ import { BaseShader } from "./BaseShader"; -import { Vector2 } from "../../three.ts/src"; +import { Vector2 } from "three.ts/src/"; import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/Chromatic.glsl"; diff --git a/src/three/shader/FXAAShader.ts b/src/three/shader/FXAAShader.ts index 3cd5aee..cf6dbeb 100644 --- a/src/three/shader/FXAAShader.ts +++ b/src/three/shader/FXAAShader.ts @@ -5,7 +5,7 @@ */ import { BaseShader } from "../.."; -import { Vector2 } from "../../three.ts/src"; +import { Vector2 } from "three.ts/src/"; import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/FXAA.glsl"; diff --git a/src/three/shader/FractalMirrorShader.ts b/src/three/shader/FractalMirrorShader.ts index 0fdb9d6..b10b3ad 100644 --- a/src/three/shader/FractalMirrorShader.ts +++ b/src/three/shader/FractalMirrorShader.ts @@ -10,7 +10,7 @@ */ import { BaseShader } from "../.."; -import { Vector2 } from "../../three.ts/src"; +import { Vector2 } from "three.ts/src/"; import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/FractalMirror.glsl"; diff --git a/src/three/shader/LuminosityHighPassShader.ts b/src/three/shader/LuminosityHighPassShader.ts index b461fcd..1121c7d 100644 --- a/src/three/shader/LuminosityHighPassShader.ts +++ b/src/three/shader/LuminosityHighPassShader.ts @@ -9,7 +9,7 @@ */ import { BaseShader } from "../.."; -import { Color } from "../../three.ts/src"; +import { Color } from "three.ts/src/"; import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/Luminosity.glsl"; diff --git a/src/three/shader/loader/index.js b/src/three/shader/loader/index.js index a0b5c77..6e403a1 100644 --- a/src/three/shader/loader/index.js +++ b/src/three/shader/loader/index.js @@ -1,7 +1,7 @@ -const fs = require('fs'); -const path = require('path'); +const fs = require("fs"); +const path = require("path"); -const glslMin = require('@yushijinhun/three-minifier-common/glsl-minifier'); +const glslMin = require("@yushijinhun/three-minifier-common/glsl-minifier"); /** * @@ -19,7 +19,7 @@ function parse(loader, source, context, cb) { imports.push({ key: match[1], target: match[0], - content: '', + content: "", }); match = importPattern.exec(source); } @@ -36,8 +36,9 @@ function parse(loader, source, context, cb) { * @param cb * @returns */ -function processImports(loader, source, context, imports, cb) { +function processImports(loader, source, context, imports, cb, lvl = 0) { // if no imports left, resolve + console.log("[GLSLoader] Walking on lvl: " + lvl); if (imports.length === 0) { return cb(null, source); } @@ -50,7 +51,7 @@ function processImports(loader, source, context, imports, cb) { } loader.addDependency(resolved); - fs.readFile(resolved, 'utf-8', (err, src) => { + fs.readFile(resolved, "utf-8", (err, src) => { if (err) { return cb(err); } @@ -63,7 +64,7 @@ function processImports(loader, source, context, imports, cb) { const newSource = source.replace(imp.target, bld); // call all imports recursively - processImports(loader, newSource, context, imports, cb); + processImports(loader, newSource, context, imports, cb, lvl++); }); }); }); @@ -98,7 +99,7 @@ function optimize(src) { /** * @param {string} source file */ -exports.default = function(source) { +exports.default = function (source) { this.cacheable(); const cb = this.async(); @@ -107,8 +108,14 @@ exports.default = function(source) { // do minifying const repl = optimize(bld); - console.log('[GLSLoader] Shortened program by: ' + (bld.length - repl.length) + ' chars'); + console.log( + "[GLSLoader] Shortened program by: " + + (bld.length - repl.length) + + " chars" + ); cb(null, `export default ${JSON.stringify(repl)}`); + + delete repl; // "gc" }); }; diff --git a/src/wasc-worker b/src/wasc-worker index bb5674c..5feacaf 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit bb5674cb163a20c8c8f0ba2a17853d757f9086e5 +Subproject commit 5feacafed64d9a705f16f7c7610e57b50cb04337 diff --git a/src/worker-loader.d.ts b/src/worker-loader.d.ts deleted file mode 100644 index f0057ee..0000000 --- a/src/worker-loader.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @author hexxone / https://hexx.one - * - * @license - * Copyright (c) 2021 hexxone All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - */ - -/** - * This is just a definition wrapper. - * - * @see https://github.com/webpack-contrib/worker-loader - */ -declare module "worker-loader!*" { - /** - * You need to change `Worker`, if: - * you specified a different value for the `workerType` option - */ - class WebpackWorker extends Worker { - constructor(options?: any); - } - - /** - * Change this if you set the `esModule` option to `false` - * // export = WebpackWorker; - */ - export default WebpackWorker; -} diff --git a/src/worker-loader.d.txt b/src/worker-loader.d.txt new file mode 100644 index 0000000..e55d2e5 --- /dev/null +++ b/src/worker-loader.d.txt @@ -0,0 +1,40 @@ +/** + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2021 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ + +/** + * This is just a definition wrapper. + * + * @see https://github.com/webpack-contrib/worker-loader + */ +declare module "worker-loader!*" { + /** + * You need to change `Worker`, if: + * you specified a different value for the `workerType` option + */ + class WebpackWorker { + constructor(options?: any); + onmessage: ((ev: MessageEvent) => any) | null; + onmessageerror: (( ev: MessageEvent) => any) | null; + /** Clones message and transmits it to worker's global environment. transfer can be passed as a list of objects that are to be transferred rather than cloned. */ + postMessage(message: any, transfer: Transferable[]): void; + postMessage(message: any, options?: StructuredSerializeOptions): void; + /** Aborts worker's associated global environment. */ + terminate(): void; + addEventListener(type: K, listener: (ev: WorkerEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: ( ev: WorkerEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + + /** + * Change this if you set the `esModule` option to `false` + * // export = WebpackWorker; + */ + export default WebpackWorker; +} From 4d5db4d47c665ae7e0029ff25fab62c8634119d8 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Mon, 26 Sep 2022 15:21:27 +0200 Subject: [PATCH 54/76] chore: circular imports --- src/CComponent.ts | 3 ++- src/CSettings.ts | 2 +- src/FPSta.ts | 5 ++++- src/LoadHelper.ts | 2 +- src/ReloadHelper.ts | 4 +++- src/Util.ts | 3 ++- src/WEICUE.ts | 7 +++++-- src/WEWA.ts | 5 ++++- src/WarnHelper.ts | 4 +++- src/XRHelper.ts | 4 +++- src/offline/OfflineHelper.ts | 2 +- src/three.ts | 2 +- src/three/EffectComposer.ts | 4 ++-- src/three/pass/RenderPass.ts | 4 ++-- src/three/pass/ShaderPass.ts | 7 +++++-- src/three/pass/UnrealBloomPass.ts | 12 +++++------- src/three/shader/BlurShader.ts | 3 ++- src/three/shader/ChromaticShader.ts | 3 ++- src/three/shader/FXAAShader.ts | 3 ++- src/three/shader/FractalMirrorShader.ts | 3 ++- src/three/shader/LuminosityHighPassShader.ts | 3 ++- src/wasc-worker | 2 +- src/weas/WEAS.ts | 15 ++++++--------- 23 files changed, 61 insertions(+), 41 deletions(-) diff --git a/src/CComponent.ts b/src/CComponent.ts index 63b96d5..73e373f 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -7,7 +7,8 @@ * See LICENSE file in the project root for full license information. */ -import { CSettings, Smallog } from "."; +import { CSettings } from "./CSettings"; +import { Smallog } from "./Smallog"; /** * Base-Component for a TypeScript Web Wallpaper diff --git a/src/CSettings.ts b/src/CSettings.ts index acc6e99..52d45a6 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -7,7 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import { Smallog } from "."; +import { Smallog } from "./Smallog"; /** * Base-Component Settings helper diff --git a/src/FPSta.ts b/src/FPSta.ts index 4219c45..e126635 100644 --- a/src/FPSta.ts +++ b/src/FPSta.ts @@ -7,7 +7,10 @@ * See LICENSE file in the project root for full license information. */ -import { CComponent, CSettings, waitReady, WEAS } from "."; +import { CComponent } from "./CComponent"; +import { CSettings } from "./CSettings"; +import { waitReady } from "./Util"; +import { WEAS } from "./weas/WEAS"; const ELM_ID = "fpstats"; diff --git a/src/LoadHelper.ts b/src/LoadHelper.ts index 06220c4..7ea7aa0 100644 --- a/src/LoadHelper.ts +++ b/src/LoadHelper.ts @@ -11,7 +11,7 @@ * */ -import { waitReady } from "."; +import { waitReady } from "./Util"; const mainColor = "#69dc00aa"; diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index 1871fbd..b962332 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -11,7 +11,9 @@ * */ -import { CComponent, CSettings, waitReady } from "."; +import { CComponent } from "./CComponent"; +import { CSettings } from "./CSettings"; +import { waitReady } from "./Util"; /** * Reload-bar settings diff --git a/src/Util.ts b/src/Util.ts index 9eee392..fd0fe64 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -9,7 +9,8 @@ * @ignore */ -import { WascInterface, wascWorker } from "."; +import { wascWorker } from "./wasc-worker"; +import { WascInterface } from "./wasc-worker/WascInterface"; // promise resolve queue const promQueue: ((val) => void)[] = []; diff --git a/src/WEICUE.ts b/src/WEICUE.ts index acc7f52..21b6d5d 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -7,9 +7,12 @@ * See LICENSE file in the project root for full license information. */ -import { CComponent, CSettings, rgbToObj, Smallog, waitReady, WEAS } from "."; - +import { CComponent } from "./CComponent"; +import { CSettings } from "./CSettings"; import { ICUE } from "./ICUE"; +import { Smallog } from "./Smallog"; +import { rgbToObj, waitReady } from "./Util"; +import { WEAS } from "./weas/WEAS"; const IMG_SRC = "./img/icue.png"; diff --git a/src/WEWA.ts b/src/WEWA.ts index 07d6e37..48821db 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -7,7 +7,10 @@ * See LICENSE file in the project root for full license information. */ -import { Smallog, waitReady, OfflineHelper, WascUtil } from "."; +import { OfflineHelper } from "./offline/OfflineHelper"; +import { Smallog } from "./Smallog"; +import { waitReady } from "./Util"; +import { WascUtil } from "./wasc-worker/WascUtil"; const LogHead = "[WEWWA] "; const DefLang = "de-de"; diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index c78a705..0c72ff1 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -7,7 +7,9 @@ * See LICENSE file in the project root for full license information. */ -import { CSettings, CComponent, waitReady } from "."; +import { CComponent } from "./CComponent"; +import { CSettings } from "./CSettings"; +import { waitReady } from "./Util"; const ELM_ID = "triggerwarn"; const IMG_SRC = "./img/triggerwarn.png"; diff --git a/src/XRHelper.ts b/src/XRHelper.ts index cbacdff..4fab226 100644 --- a/src/XRHelper.ts +++ b/src/XRHelper.ts @@ -4,8 +4,10 @@ * @author Mugen87 / https://github.com/Mugen87 */ -import { CSettings, CComponent, waitReady } from "."; import { XRSessionInit } from "three.ts/src/XRWebGL"; +import { CComponent } from "./CComponent"; +import { CSettings } from "./CSettings"; +import { waitReady } from "./Util"; /** * XR Settings diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index 3c2d418..a05c0bc 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -7,7 +7,7 @@ * See LICENSE file in the project root for full license information. */ -import { Smallog } from ".."; +import { Smallog } from "../Smallog"; // this should pack the serviceworker like a webwoker. diff --git a/src/three.ts b/src/three.ts index 69a3c35..299ad35 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 69a3c35b945e670a633a6f485f115dd9c32e977e +Subproject commit 299ad35e5c18a89983884434dfc80ccc6cfb2733 diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts index c8e7d5d..e9dfbc8 100644 --- a/src/three/EffectComposer.ts +++ b/src/three/EffectComposer.ts @@ -4,8 +4,6 @@ * @author hexxone / https://hexx.one */ -import { RenderPass, BasePass } from ".."; - import { LinearFilter, PerspectiveCamera, @@ -16,6 +14,8 @@ import { WebGLRenderer, WebGLRenderTarget, } from "three.ts/src/"; +import { BasePass } from "./pass/BasePass"; +import { RenderPass } from "./pass/RenderPass"; /** * render shader chain diff --git a/src/three/pass/RenderPass.ts b/src/three/pass/RenderPass.ts index e05cf32..0a298b8 100644 --- a/src/three/pass/RenderPass.ts +++ b/src/three/pass/RenderPass.ts @@ -4,8 +4,6 @@ * @author hexxone / https://hexx.one */ -import { BasePass } from "../.."; - import { Camera, Color, @@ -15,6 +13,8 @@ import { WebGLRenderTarget, } from "three.ts/src/"; +import { BasePass } from "./BasePass"; + /** * Shader Render Helper */ diff --git a/src/three/pass/ShaderPass.ts b/src/three/pass/ShaderPass.ts index f0acf37..397316f 100644 --- a/src/three/pass/ShaderPass.ts +++ b/src/three/pass/ShaderPass.ts @@ -4,8 +4,6 @@ * @author hexxone / https://hexx.one */ -import { BasePass, BaseShader, FullScreenHelper } from "../.."; - import { ShaderMaterial, UniformsUtils, @@ -14,6 +12,11 @@ import { WebGLRenderTarget, } from "three.ts/src/"; +import { BaseShader } from "../shader/BaseShader"; + +import { BasePass } from "./BasePass"; +import { FullScreenHelper } from "./FullScreenHelper"; + /** * ThreeJS Pass for easy full screen shaders */ diff --git a/src/three/pass/UnrealBloomPass.ts b/src/three/pass/UnrealBloomPass.ts index 53162ec..67f7f2a 100644 --- a/src/three/pass/UnrealBloomPass.ts +++ b/src/three/pass/UnrealBloomPass.ts @@ -2,13 +2,6 @@ * @author spidersharma / http://eduperiment.com/ */ -import { - BasePass, - CopyShader, - FullScreenHelper, - LuminosityHighPassShader, -} from "../.."; - import { AdditiveBlending, Color, @@ -22,6 +15,11 @@ import { WebGLRenderer, WebGLRenderTarget, } from "three.ts/src/"; +import { CopyShader } from "../shader/CopyShader"; +import { LuminosityHighPassShader } from "../shader/LuminosityHighPassShader"; + +import { BasePass } from "./BasePass"; +import { FullScreenHelper } from "./FullScreenHelper"; /** * Inspired from Unreal Engine diff --git a/src/three/shader/BlurShader.ts b/src/three/shader/BlurShader.ts index 4137ba7..bfd1ff2 100644 --- a/src/three/shader/BlurShader.ts +++ b/src/three/shader/BlurShader.ts @@ -7,9 +7,10 @@ * See LICENSE file in the project root for full license information. */ -import { BaseShader } from "../.."; import { Vector2 } from "three.ts/src/"; +import { BaseShader } from "./BaseShader"; + import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/Blur.glsl"; diff --git a/src/three/shader/ChromaticShader.ts b/src/three/shader/ChromaticShader.ts index 5673e23..e7e6a3e 100644 --- a/src/three/shader/ChromaticShader.ts +++ b/src/three/shader/ChromaticShader.ts @@ -7,9 +7,10 @@ * See LICENSE file in the project root for full license information. */ -import { BaseShader } from "./BaseShader"; import { Vector2 } from "three.ts/src/"; +import { BaseShader } from "./BaseShader"; + import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/Chromatic.glsl"; diff --git a/src/three/shader/FXAAShader.ts b/src/three/shader/FXAAShader.ts index cf6dbeb..073ff3a 100644 --- a/src/three/shader/FXAAShader.ts +++ b/src/three/shader/FXAAShader.ts @@ -4,9 +4,10 @@ * @author hexxone / https://hexx.one */ -import { BaseShader } from "../.."; import { Vector2 } from "three.ts/src/"; +import { BaseShader } from "./BaseShader"; + import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/FXAA.glsl"; diff --git a/src/three/shader/FractalMirrorShader.ts b/src/three/shader/FractalMirrorShader.ts index b10b3ad..160e1b8 100644 --- a/src/three/shader/FractalMirrorShader.ts +++ b/src/three/shader/FractalMirrorShader.ts @@ -9,9 +9,10 @@ * @description */ -import { BaseShader } from "../.."; import { Vector2 } from "three.ts/src/"; +import { BaseShader } from "./BaseShader"; + import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/FractalMirror.glsl"; diff --git a/src/three/shader/LuminosityHighPassShader.ts b/src/three/shader/LuminosityHighPassShader.ts index 1121c7d..9f096c8 100644 --- a/src/three/shader/LuminosityHighPassShader.ts +++ b/src/three/shader/LuminosityHighPassShader.ts @@ -8,9 +8,10 @@ * See LICENSE file in the project root for full license information. */ -import { BaseShader } from "../.."; import { Color } from "three.ts/src/"; +import { BaseShader } from "./BaseShader"; + import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/Luminosity.glsl"; diff --git a/src/wasc-worker b/src/wasc-worker index 5feacaf..047fbe1 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 5feacafed64d9a705f16f7c7610e57b50cb04337 +Subproject commit 047fbe12ce692e493ca3acc00be4f86e76ec538e diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 67269dc..14a7a38 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -7,15 +7,12 @@ * See LICENSE file in the project root for full license information. */ -import { - CComponent, - CSettings, - Smallog, - waitReady, - WascInterface, - sharedWorker, - Bea_ts, -} from ".."; +import { CComponent } from "../CComponent"; +import { CSettings } from "../CSettings"; +import { Smallog } from "../Smallog"; +import { sharedWorker, waitReady } from "../Util"; +import { WascInterface } from "../wasc-worker/WascInterface"; +import { Bea_ts } from "./Bea"; const DAT_LEN = 128; const LISTENAME = "wallpaperRegisterAudioListener"; From 95a6eb9dfbc55c8dc43257b0ad45c57a8a042449 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Thu, 29 Sep 2022 02:21:58 +0200 Subject: [PATCH 55/76] fix: less logging --- src/renamer/RenamerPlugin.js | 27 ++++++++++++++------------- src/three.ts | 2 +- src/three/shader/loader/index.js | 3 ++- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js index bc0d484..38331f1 100644 --- a/src/renamer/RenamerPlugin.js +++ b/src/renamer/RenamerPlugin.js @@ -227,15 +227,15 @@ class RenamerPlugin { // replace all occurences for (let index = 0; index < filtered.length; index++) { const element = filtered[index]; - console.log( - "Replacing " + - element.k + - " => " + - element.r + - " (" + - element.v + - " usages)" - ); + // console.log( + // "Replacing " + + // element.k + + // " => " + + // element.r + + // " (" + + // element.v + + // " usages)" + // ); let skipped = 0; let newPd = pd; @@ -321,7 +321,8 @@ class RenamerPlugin { } function mayEncode(str) { - if (Math.random() > 0.5) { + var forced = true; // TODO + if (Math.random() > 0.5 || forced) { return lib.JSFuck.encode(str); } return `'${str}'`; @@ -336,9 +337,9 @@ class RenamerPlugin { const fk4 = mayEncode("length"); const fk6 = lib.JSFuck.encode("atob"); const fk7 = mayEncode("String"); - const tmp1 = "`fromC${ë}`"; - const tmp2 = "`c${ë}At`"; - const func = `é=>{var è=window,ë='harCode',ě=${fk4},ė=è,è='';for(var ê=0,é=ė[${fk6}](é);ê<é[ě];è+=ė[${fk7}][${tmp1}]((é[ê++][${tmp2}]())-(${fk1}))){}return è[${fk2}](${fk3})}`; + const tmp1 = "`fromC${Σ}`"; + const tmp2 = "`c${Σ}At`"; + const func = `Ⅾ=>{var Ή=window,Σ='harCode',Ⅹ=${fk4},χ=Ή,Ή='';for(var Ἰ=0,Ⅾ=χ[${fk6}](Ⅾ);Ἰ<Ⅾ[Ⅹ];Ή+=χ[${fk7}][${tmp1}]((Ⅾ[Ἰ++][${tmp2}]())-(${fk1}))){}return Ή[${fk2}](${fk3})}`; newStrict += `var ${splFunc}=${func},`; } diff --git a/src/three.ts b/src/three.ts index 299ad35..98e1d9a 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 299ad35e5c18a89983884434dfc80ccc6cfb2733 +Subproject commit 98e1d9a06d570a30b96f19056489dfe85421d96d diff --git a/src/three/shader/loader/index.js b/src/three/shader/loader/index.js index 6e403a1..f867475 100644 --- a/src/three/shader/loader/index.js +++ b/src/three/shader/loader/index.js @@ -38,7 +38,8 @@ function parse(loader, source, context, cb) { */ function processImports(loader, source, context, imports, cb, lvl = 0) { // if no imports left, resolve - console.log("[GLSLoader] Walking on lvl: " + lvl); + if(lvl > 0) + console.log("[GLSLoader] Walking on lvl: " + lvl); if (imports.length === 0) { return cb(null, source); } From 05b5e4f8d6386ddde62fe21580319c0c4c06be54 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Fri, 7 Oct 2022 19:54:46 +0200 Subject: [PATCH 56/76] fix: License & worker --- README.md | 9 +- src/CComponent.ts | 2 +- src/CSettings.ts | 2 +- src/FPSta.ts | 119 ++++++++++++++++--- src/LoadHelper.ts | 2 +- src/ReloadHelper.ts | 2 +- src/Smallog.ts | 23 +++- src/Util.ts | 2 +- src/WEICUE.ts | 2 +- src/WEWA.ts | 95 +++++++-------- src/WarnHelper.ts | 2 +- src/index.ts | 12 +- src/offline/Offline.worker.ts | 4 +- src/offline/OfflineHelper.ts | 8 +- src/offline/OfflinePlugin.js | 2 +- src/renamer/RenamerPlugin.js | 28 ++--- src/three.ts | 2 +- src/three/EffectComposer.ts | 6 +- src/three/pass/UnrealBloomPass.ts | 1 + src/three/shader/BaseShader.ts | 2 +- src/three/shader/BlendShader.ts | 2 +- src/three/shader/BlurShader.ts | 2 +- src/three/shader/ChromaticShader.ts | 2 +- src/three/shader/CopyShader.ts | 2 +- src/three/shader/FractalMirrorShader.ts | 2 +- src/three/shader/LUTShader.ts | 2 +- src/three/shader/LUTShaderNearest.ts | 2 +- src/three/shader/LuminosityHighPassShader.ts | 2 +- src/wasc-worker | 2 +- src/weas/WEAS.ts | 11 +- src/weas/index.ts | 4 +- src/worker-loader.d.ts | 29 +++++ src/worker-loader.d.txt | 40 ------- 33 files changed, 270 insertions(+), 157 deletions(-) create mode 100644 src/worker-loader.d.ts delete mode 100644 src/worker-loader.d.txt diff --git a/README.md b/README.md index 6820a0f..a7eb35b 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ # [we_utils](https://github.com/hexxone/we_utils) -### is a collection of utilities, mostly usefull when creating Wallpaper Engine Web Wallpapers with TypeScript / Webpack. +## A collection of utilities, mostly usefull when creating Wallpaper-Engine Web-Wallpapers with TypeScript & Webpack I created this repository since I was previously copying back-and-forth lots of code between projects. Keeping track of this stuff manually is annoying... - ## [Documentation](https://hexxone.github.io/we_utils) - ### Dependencies / Libraries + - [TypeScript](https://www.typescriptlang.org/) for typization - [three.js](https://threejs.org/) & Examples for webgl rendering - [WebAssembly](https://webassembly.org/) for more efficient processing @@ -17,8 +16,8 @@ Keeping track of this stuff manually is annoying... - [wasc-worker](https://github.com/hexxone/wasc-worker) for ez AssemblyScript workers - [cookieconsent](https://github.com/osano/cookieconsent) thanks to EU laws - ### Features / Contents + - OfflineWorker - AssemblyScript Webpack Builder - AssemblyScript WebAssembly Module Loader @@ -33,7 +32,7 @@ Keeping track of this stuff manually is annoying... - Worker-Loader definition file - Stats.js definition file - ### Used by + - [AudiOrbits](https://github.com/hexxone/audiorbits) - [ReactiveInk](https://github.com/hexxone/ReactiveInk) diff --git a/src/CComponent.ts b/src/CComponent.ts index 73e373f..cb11d36 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/CSettings.ts b/src/CSettings.ts index 52d45a6..c3515b4 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/FPSta.ts b/src/FPSta.ts index e126635..3157150 100644 --- a/src/FPSta.ts +++ b/src/FPSta.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ @@ -12,7 +12,9 @@ import { CSettings } from "./CSettings"; import { waitReady } from "./Util"; import { WEAS } from "./weas/WEAS"; -const ELM_ID = "fpstats"; +const Element_Id = "fpstats"; +const Mem_Update_Rate = 19; + /** * Custom Stats settings @@ -25,6 +27,7 @@ export class FPSettings extends CSettings { /** * Custom FPS Stats module + * * @public * @extends {CComponent} */ @@ -35,25 +38,34 @@ export class FPStats extends CComponent { // FPS private fpsHolder: HTMLElement; private lastUpdate: number = performance.now(); - private frameCount = 0; + private frameCount = 1; // usage private useHolder: HTMLElement; // cpu private cpuHolder: HTMLElement; private cpuBegin: number = performance.now(); private cpuEnd: number = performance.now(); - private cpuMS = 0; + private cpuMS = 1; // gpu private gpuHolder: HTMLElement; private gpuBegin: number = performance.now(); private gpuEnd: number = performance.now(); - private gpuMS = 0; + private gpuMS = 1; // audio private auProvider: WEAS = null; private audHolder: HTMLElement; - private audioMS = 0; + private audioMS = 1; private bpmHolder: HTMLDivElement; + // memory + private memUpdate = 0; + private perfMemHolder?: HTMLElement; + private gpuMemHolder?: HTMLElement; + private domMemHolder?: HTMLElement; + private wrkMemHolder?: HTMLElement; // TODO + + // TODO benchmark mode + /** * Create hidden element * @param {WEAS} audio (optional) @@ -76,7 +88,7 @@ export class FPStats extends CComponent { private injectCSS() { const st = document.createElement("style"); st.innerHTML = ` - #${ELM_ID} { + #${Element_Id} { opacity: 0; position: fixed; top: 50vh; @@ -87,7 +99,7 @@ export class FPStats extends CComponent { text-align: left; background: black; } - #${ELM_ID}.show { + #${Element_Id}.show { opacity: 0.8; } `; @@ -101,7 +113,7 @@ export class FPStats extends CComponent { private injectHTML() { // root this.container = document.createElement("div"); - this.container.id = ELM_ID; + this.container.id = Element_Id; document.body.append(this.container); // fps this.fpsHolder = document.createElement("div"); @@ -147,6 +159,7 @@ export class FPStats extends CComponent { }); } + /** * Start measuring interval * @public @@ -191,15 +204,18 @@ export class FPStats extends CComponent { // calculate const elapsd = (now - this.lastUpdate) / 1000; const efpies = this.frameCount / elapsd; - const yusage = (this.cpuMS + this.gpuMS) / 500; - const cepeyu = this.cpuMS / this.frameCount; - const gepeyu = this.gpuMS / this.frameCount; + + const target = this.getFpsTarget(efpies); + const msPerFps = (1000 / target); + const cepeyu = this.cpuMS / this.frameCount / msPerFps * 100; + const gepeyu = this.gpuMS / this.frameCount / msPerFps * 100; + const alluse = (target / efpies) * (cepeyu + gepeyu); // apply - this.fpsHolder.innerText = `FPS: ${efpies.toFixed(2)}`; - this.cpuHolder.innerText = `CPU: ${cepeyu.toFixed(2)} ms`; - this.gpuHolder.innerText = `GPU: ${gepeyu.toFixed(2)} ms`; - this.useHolder.innerText = `All: ${yusage.toFixed(2)} %`; + this.fpsHolder.innerText = `FPS: ${efpies.toFixed(2)} / ${target}`; + this.cpuHolder.innerText = `CPU: ${cepeyu.toFixed(2)} %`; + this.gpuHolder.innerText = `GPU: ${gepeyu.toFixed(2)} %`; + this.useHolder.innerText = `All: ${alluse.toFixed(2)} %`; if (this.audHolder) this.audHolder.innerText = `Audio: ${this.audioMS.toFixed(2)} ms`; @@ -215,6 +231,11 @@ export class FPStats extends CComponent { this.bpmHolder.innerText = `BPM: ${bts.toFixed(2)} ~`; } + if (this.memUpdate++ > Mem_Update_Rate) { + this.memUpdate = 0; + this.updateMemory(); + } + this.lastUpdate = now; this.reset(); } @@ -224,6 +245,70 @@ export class FPStats extends CComponent { * @public */ public reset() { - this.frameCount = this.cpuMS = this.gpuMS = this.audioMS = 0; + this.frameCount = this.cpuMS = this.gpuMS = this.audioMS = 1; + } + + + private updateMemory() { + if (window.crossOriginIsolated && performance["measureUserAgentSpecificMemory"] !== undefined) { + performance["measureUserAgentSpecificMemory"]().then(result => { + if (result && result.breakdown && result.bytes) { + let sum = result.bytes; + if (isNaN(sum) || sum <= 0) { + console.warn("Invalid performance result: ", result); + return; + } + + if (!this.perfMemHolder) { + this.perfMemHolder = document.createElement("div"); + this.useHolder.insertAdjacentElement("afterend", this.perfMemHolder); + } + + const getDetail = (type: string) => + result.breakdown.find(bd => !!bd && !!bd.types && !!bd.types.includes && bd.types.includes(type)) ?? null + + const addDetail = (gotDetail: any, text: string, gotElement: HTMLElement, insertAfter: HTMLElement) => { + if (!!gotDetail && !isNaN(gotDetail.bytes)) { + if (!gotElement) { + gotElement = document.createElement("div"); + insertAfter.insertAdjacentElement("afterend", gotElement); + } + const domMem = gotDetail.bytes; + sum -= domMem; + gotElement.innerText = `${text}: ${this.formatBytes(domMem)}`; + } + return gotElement; + } + + this.domMemHolder = addDetail(getDetail("DOM"), "DOM", this.domMemHolder, this.perfMemHolder); + + this.gpuMemHolder = addDetail(getDetail("Canvas"), "VRAM", this.gpuMemHolder, this.perfMemHolder); + + + this.perfMemHolder.innerText = `RAM: ${this.formatBytes(sum)}`; + } + }).catch(err => console.error); + } } + + private getFpsTarget(current: number) { + const margin = current * 0.1; + const targets = [600, 480, 360, 240, 144, 120, 90, 75, 60, 30]; + for (let i = 1; i < targets.length; i++) { + if(current > targets[i] + margin) + return targets[i - 1]; + } + return 30; + } + + private formatBytes(n: number) { + const units = ['b', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + let l = 0; + while (n >= 1024 && ++l) { + n = n / 1024; + } + return (n.toFixed(l > 0 ? 2 : 0) + ' ' + units[l]); + } + + } diff --git a/src/LoadHelper.ts b/src/LoadHelper.ts index 7ea7aa0..c5df575 100644 --- a/src/LoadHelper.ts +++ b/src/LoadHelper.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index b962332..0be5da2 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/Smallog.ts b/src/Smallog.ts index 57a8c29..f22c015 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ @@ -18,14 +18,18 @@ export enum LogLevel { * Print only error messages */ Error = 0, + /** + * Print warnings and Info + */ + Warn = 1, /** * Print error and info messages */ - Info = 1, + Info = 2, /** * Print all messages */ - Debug = 2, + Debug = 3, } /** @@ -95,6 +99,19 @@ class Smalog { console.error(hdr + msg); } + /** + * print info message + * @param {string} msg log + * @param {string} hdr overwrite header + * @return {void} nothing + */ + warn(msg: string, hdr: string = this.preFix) { + if (this.logLevel >= 1) { + if (this.printTime) msg = "[" + new Date().toLocaleString() + "] " + msg; + if (this.logLevel >= 2) hdr = this.traceCall(hdr); + console.warn(hdr + msg); + } + } /** * print info message * @param {string} msg log diff --git a/src/Util.ts b/src/Util.ts index fd0fe64..fa0bb1f 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 21b6d5d..7d67e2f 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/WEWA.ts b/src/WEWA.ts index 48821db..0428ad1 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ @@ -14,53 +14,37 @@ import { WascUtil } from "./wasc-worker/WascUtil"; const LogHead = "[WEWWA] "; const DefLang = "de-de"; -const wral = "wallpaperRegisterAudioListener"; -const proj = "project.json"; /** * WEWWA - *
      + * * Wallpaper Engine Web Wallpaper Adapter - *
      + * * This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine * Web-Wallpaper project - so you can test, run & configure it from a normal web browser. - *
      + * * REQUIREMENTS: - *
      + * * - HTML5 Browser - *
      * - the "project.json" needs to be in the root folder like "index.html" - *
      * - this file needs to be included/built in your "index.html" - *
      - *
      + * + * * FEATURES: - *
      + * * - automatically detecting if the web wallpaper is opened by wallpaper engine or browser - *
      * - if opened by wallpaper engine, nothing will happen - *
      * - if opened by a browser: - *
      * - use a ServiceWorker to make page always available offline - *
      * - automatically load the "project.json" - *
      * - parse the settings, languages & conditions - *
      * - add respective html elements for each setting type & condition - *
      * - put these elements into an option menu which can be hidden - *
      * - check localStorage for already saved/customized values - *
      * - apply all settings once - *
      * - react to changes made in the ui and update them in the wallpaper - *
      * - save changes made in the ui to localStorage * - * * @todo * - inject "audio processing" setting * @@ -73,7 +57,14 @@ const proj = "project.json"; * @public */ export class WEWWA { - private project: any = null; + + private audListener: string; + private propListener: string; + + private projectFile: string; + private defLang: string; + + private projectData: any = null; private htmlMenu: Element = null; private htmlIcon: Element = null; @@ -94,8 +85,18 @@ export class WEWWA { * if yes => iniitialize, else => do nothing * @param {Function} finished Callback for initializing the wallpaper */ - constructor(finished) { - if (window[wral]) { + constructor( + audListener = "wallpaperRegisterAudioListener", + propListener = "wallpaperPropertyListener", + projFile = "project.json", defLang = "de-de", + finished?: () => {} | null) { + + this.audListener = audListener; + this.propListener = propListener; + this.projectFile = projFile; + this.defLang = defLang; + + if (window[audListener]) { Smallog.info("detected wallpaper engine => Standby.", LogHead); finished(); return; @@ -104,7 +105,7 @@ export class WEWWA { Smallog.info("wallpaper engine not detected => Init!", LogHead); // define audio listener first, so we dont miss when it gets registered. - window[wral] = (callback) => { + window[audListener] = (callback) => { // set callback to be called later with analysed audio data this.audioCallback = callback; Smallog.info("Registered wallpaper AudioListener.", LogHead); @@ -115,7 +116,7 @@ export class WEWWA { // make the website available offline using service worker OfflineHelper.register(document.title.replace(" ", "")).then(() => { // continue initializing - finished(); + if (finished !== undefined) finished(); this.init(); // pause and resume on focus events @@ -131,10 +132,10 @@ export class WEWWA { * @returns {void} */ private init() { - WascUtil.myFetch(proj, "json").then((proj) => { - if (proj.type != "web") { + WascUtil.myFetch(this.projectFile, "json").then((json) => { + if (json.type != "web") { Smallog.error( - `Error! Loaded ${proj} is not a web Wallpaper. How did this happen? Aborting...`, + `Error! Loaded ${json} is not a web Wallpaper. How did this happen? Aborting...`, LogHead ); return; @@ -142,12 +143,12 @@ export class WEWWA { // new NDBG(); - this.project = proj; + this.projectData = json; this.loadStorage(); this.addStyle(); this.addMenu(localStorage.getItem("wewwaLang")); this.evaluateSettings(); - this.applyProp(proj.general.properties); + this.applyProp(json.general.properties); }); } @@ -157,15 +158,15 @@ export class WEWWA { * @returns {void} */ private loadStorage() { - const props = this.project.general.properties; + const props = this.projectData.general.properties; const last = localStorage.getItem("wewwaLastProps"); if (last != null) { const merged = Object.assign(props, JSON.parse(last)); merged.audioprocessing = { - value: this.project.general.supportsaudioprocessing, + value: this.projectData.general.supportsaudioprocessing, type: "hidden", }; - this.project.general.properties = merged; + this.projectData.general.properties = merged; Smallog.debug("Loaded & merged settings.", LogHead); } } @@ -320,7 +321,7 @@ export class WEWWA { const ce = (e) => document.createElement(e); // local vars faster - const proj = this.project; + const proj = this.projectData; const props = proj.general.properties; // create root menu @@ -704,7 +705,7 @@ export class WEWWA { link.setAttribute( "href", "https://steamcommunity.com/sharedfiles/filedetails/?id=" + - proj.workshopid + proj.workshopid ); link.setAttribute("target", "_blank"); link.innerHTML = "

      Open Workshop Page

      "; @@ -863,7 +864,7 @@ export class WEWWA { */ public setProperty(prop, elm) { // get the type and apply the value - const props = this.project.general.properties; + const props = this.projectData.general.properties; // check for legit setting... if (!props[prop]) { @@ -939,7 +940,7 @@ export class WEWWA { public evaluateSettings() { // dynamic prefix for evaluation const pre = "wewwaProps"; - const wewwaProps = this.project.general.properties; + const wewwaProps = this.projectData.general.properties; localStorage.setItem("wewwaLastProps", JSON.stringify(wewwaProps)); for (const p in wewwaProps) { @@ -1017,7 +1018,7 @@ export class WEWWA { * @public */ public applyProp(prop) { - const wpl = window["wallpaperPropertyListener"]; + const wpl = window[this.propListener]; if (wpl && wpl.applyUserProperties) { wpl.applyUserProperties(prop); } @@ -1029,7 +1030,7 @@ export class WEWWA { * @public */ public setPaused(val: boolean) { - const wpl = window["wallpaperPropertyListener"]; + const wpl = window[this.propListener]; if (this.isPaused == val) return; if (val && !this.pauseOnUnfocus) return; if (wpl && wpl.setPaused) { @@ -1058,10 +1059,10 @@ export class WEWWA { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? [ - parseInt(result[1], 16) / 255, - parseInt(result[2], 16) / 255, - parseInt(result[3], 16) / 255, - ].join(" ") + parseInt(result[1], 16) / 255, + parseInt(result[2], 16) / 255, + parseInt(result[3], 16) / 255, + ].join(" ") : null; } diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index 0c72ff1..ad8b76f 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/index.ts b/src/index.ts index a509a26..7595e87 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,13 @@ +/** + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2022 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + * + */ + // Offline-Worker entry-point export * from "./offline/OfflineHelper"; @@ -5,7 +15,7 @@ export * from "./offline/OfflineHelper"; export * from "./three"; // Web-AssemblyScript entry-point -export * from "./wasc-worker"; +export * from "./wasc-worker/"; // basic modules export * from "./CSettings"; diff --git a/src/offline/Offline.worker.ts b/src/offline/Offline.worker.ts index 2c9a20e..cb3c8b9 100644 --- a/src/offline/Offline.worker.ts +++ b/src/offline/Offline.worker.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * @@ -23,7 +23,7 @@ const wrk: ServiceWorker = self as any; // allowing you to remove outdated cache entries during the update. const wName = "[OfflineWorker]"; const version = "::2.4"; -console.info(wName + "executing."); +console.info(wName + " executing.."); // The install event fires when the service worker is first installed. // You can use this event to prepare the service worker to be able to serve diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index a05c0bc..988fecc 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ @@ -11,8 +11,8 @@ import { Smallog } from "../Smallog"; // this should pack the serviceworker like a webwoker. -// import OfflineWorker from "worker-loader!./Offline.worker"; -const OfflineWorker = () => new Worker(new URL("./Offline.worker.js", import.meta.url)); +import OfflineWorker from "worker-loader!./Offline.worker"; +// const OfflineWorker = () => new Worker(new URL(, import.meta.url)); const oh = "[OfflineHelper] "; @@ -36,7 +36,7 @@ const oh = "[OfflineHelper] "; // eslint-disable-next-line require-jsdoc // eslint-disable-next-line @typescript-eslint/no-unused-vars function DontRemove() { - return OfflineWorker(); + return new OfflineWorker(); } /** diff --git a/src/offline/OfflinePlugin.js b/src/offline/OfflinePlugin.js index d770138..2a5f56a 100644 --- a/src/offline/OfflinePlugin.js +++ b/src/offline/OfflinePlugin.js @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * @ignore diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js index 38331f1..0fa6abd 100644 --- a/src/renamer/RenamerPlugin.js +++ b/src/renamer/RenamerPlugin.js @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * @@ -17,7 +17,7 @@ const lib = require("./jsfuck.js"); const { RawSource } = require("webpack-sources"); const validate = require("schema-utils"); -const pluginName = "RenamerPlugin"; +const pluginName = "[RenamerPlugin]"; /** * schema for options object @@ -298,11 +298,11 @@ class RenamerPlugin { // sanity check // @todo bruh if (newPd.match(/\]\][a-zA-Z0-9_]{1,}/)) { - console.log("Error double bracket, missing delimiter? "); + console.log(pluginName + " Error double bracket, missing delimiter? "); console.log(element); } else { if (skipped > 0) - console.log(`Skipped ${skipped} invalid replacements.`); + console.log(`${pluginName} Skipped ${skipped} invalid replacements.`); pd = newPd; } } @@ -363,20 +363,20 @@ class RenamerPlugin { newStrict += `${k}=${val}`; }); - console.log(newStrict); + console.log(pluginName + newStrict); pd = newStrict + ";" + pd; const lengDiff = oldPd.length - pd.length; const expDiff = lengDiff - sum; - console.log("Replacement complete!"); + console.log(pluginName + " Replacement complete!"); let m = "Actually saved: " + lengDiff + " chars. "; if (expDiff > 0) m += " (" + expDiff + " more than expected)"; if (expDiff < 0) m += " (" + Math.abs(expDiff) + " less than expected)"; console.log(m); if (lengDiff < 0) { - console.log("Did not save any chars! rolling back..."); + console.log(pluginName + " Did not save any chars! rolling back..."); pd = oldPd; } @@ -390,7 +390,7 @@ class RenamerPlugin { */ processString(source) { if (typeof source !== "string") { - console.error("Source no string: ", source); + console.error(pluginName + " Source no string: ", source); return source; } @@ -412,7 +412,7 @@ class RenamerPlugin { */ processSource(compilation, name, child) { if (child._valueIsBuffer || !child.source) { - console.log("Value is Buffer!"); + console.log(pluginName + " Value is Buffer!"); return; } const source = child.source._value; @@ -423,7 +423,7 @@ class RenamerPlugin { compilation.updateAsset(name, new RawSource(processed)); // calculate saved memory const savedChars = source.length - processed.length; - console.info("[" + pluginName + "] Saved: " + savedChars + " chars"); + console.info(pluginName + " Saved: " + savedChars + " chars"); } } @@ -436,12 +436,12 @@ class RenamerPlugin { apply(compiler) { compiler.hooks.emit.tap(pluginName, (compilation) => { try { - console.info("[" + pluginName + "] Using Regex: " + this.options.regex); + console.info(pluginName + " Using Regex: " + this.options.regex); // process all compiled .js files for (const assetFile in compilation.assets) { if (!assetFile || !assetFile.endsWith(".js")) continue; - console.info("[" + pluginName + "] Processing: " + assetFile); + console.info(pluginName + " Processing: " + assetFile); // get the processed asset object / source const asset = compilation.getAsset(assetFile); @@ -456,9 +456,9 @@ class RenamerPlugin { } // finish up - console.info("[" + pluginName + "] Replaced: ", this.nameMap); + console.info(pluginName + " Replaced: ", this.nameMap); } catch (error) { - console.info("[" + pluginName + "] Replace error: ", error); + console.info(pluginName + " Replace error: ", error); } }); } diff --git a/src/three.ts b/src/three.ts index 98e1d9a..e9cfd1c 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 98e1d9a06d570a30b96f19056489dfe85421d96d +Subproject commit e9cfd1c0459d1898125bce98b5143691d037c31c diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts index e9dfbc8..3130c19 100644 --- a/src/three/EffectComposer.ts +++ b/src/three/EffectComposer.ts @@ -14,6 +14,7 @@ import { WebGLRenderer, WebGLRenderTarget, } from "three.ts/src/"; + import { BasePass } from "./pass/BasePass"; import { RenderPass } from "./pass/RenderPass"; @@ -267,7 +268,10 @@ export class EffectComposer { this.renderer.xr.enabled = true; } else { // render default - this.camera.rotation.set(0, 0, 0); + + // TODO really needed? + // this.camera.rotation.set(0, 0, 0); + this.renderer.setScissor(0, 0, size.width, size.height); this.renderer.setViewport(0, 0, size.width, size.height); // pass buffers flipped to avoid swap diff --git a/src/three/pass/UnrealBloomPass.ts b/src/three/pass/UnrealBloomPass.ts index 67f7f2a..662a5e8 100644 --- a/src/three/pass/UnrealBloomPass.ts +++ b/src/three/pass/UnrealBloomPass.ts @@ -15,6 +15,7 @@ import { WebGLRenderer, WebGLRenderTarget, } from "three.ts/src/"; + import { CopyShader } from "../shader/CopyShader"; import { LuminosityHighPassShader } from "../shader/LuminosityHighPassShader"; diff --git a/src/three/shader/BaseShader.ts b/src/three/shader/BaseShader.ts index 9d51d88..1d62bc3 100644 --- a/src/three/shader/BaseShader.ts +++ b/src/three/shader/BaseShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license -* Copyright (c) 2021 hexxone All rights reserved. +* Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/BlendShader.ts b/src/three/shader/BlendShader.ts index 45e4f76..4360fdd 100644 --- a/src/three/shader/BlendShader.ts +++ b/src/three/shader/BlendShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license -* Copyright (c) 2021 hexxone All rights reserved. +* Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/BlurShader.ts b/src/three/shader/BlurShader.ts index bfd1ff2..c3e532b 100644 --- a/src/three/shader/BlurShader.ts +++ b/src/three/shader/BlurShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/ChromaticShader.ts b/src/three/shader/ChromaticShader.ts index e7e6a3e..69ce30a 100644 --- a/src/three/shader/ChromaticShader.ts +++ b/src/three/shader/ChromaticShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/CopyShader.ts b/src/three/shader/CopyShader.ts index 0c64908..829b3ec 100644 --- a/src/three/shader/CopyShader.ts +++ b/src/three/shader/CopyShader.ts @@ -3,7 +3,7 @@ * @author hexxone / https://hexx.one * * @license -* Copyright (c) 2021 hexxone All rights reserved. +* Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/FractalMirrorShader.ts b/src/three/shader/FractalMirrorShader.ts index 160e1b8..44539a1 100644 --- a/src/three/shader/FractalMirrorShader.ts +++ b/src/three/shader/FractalMirrorShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/three/shader/LUTShader.ts b/src/three/shader/LUTShader.ts index 11e5ce4..cdcdd9e 100644 --- a/src/three/shader/LUTShader.ts +++ b/src/three/shader/LUTShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license -* Copyright (c) 2021 hexxone All rights reserved. +* Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/LUTShaderNearest.ts b/src/three/shader/LUTShaderNearest.ts index 18db147..754c87b 100644 --- a/src/three/shader/LUTShaderNearest.ts +++ b/src/three/shader/LUTShaderNearest.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license -* Copyright (c) 2021 hexxone All rights reserved. +* Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/LuminosityHighPassShader.ts b/src/three/shader/LuminosityHighPassShader.ts index 9f096c8..fd0c45b 100644 --- a/src/three/shader/LuminosityHighPassShader.ts +++ b/src/three/shader/LuminosityHighPassShader.ts @@ -3,7 +3,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/wasc-worker b/src/wasc-worker index 047fbe1..fc4e600 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 047fbe12ce692e493ca3acc00be4f86e76ec538e +Subproject commit fc4e6007fd89463898ca10f0d4ac40caee59d6c0 diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 14a7a38..0ccfef1 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ @@ -136,6 +136,9 @@ export class WEAS extends CComponent { /** @public transfer buffer (last raw data) */ public inBuff = new Float64Array(DAT_LEN); + // has context been set? + initialized = false; + // web assembly functions weasModule: WascInterface = null; @@ -185,7 +188,7 @@ export class WEAS extends CComponent { private async realInit() { // only listen if wallpaper engine context given if (!window[LISTENAME]) { - Smallog.error("'window.wallpaperRegisterAudioListener' not given!"); + Smallog.warn("'window.wallpaperRegisterAudioListener' not given!"); return; } this.init = null; @@ -203,6 +206,7 @@ export class WEAS extends CComponent { this.updateSettings().then(() => { this.registerListener(); + this.initialized = true; Smallog.debug("WebAssembly Audio provider is ready!"); }); }) @@ -442,6 +446,7 @@ export class WEAS extends CComponent { if (this.settings.show_canvas) this.updateCanvas(); return ( + this.initialized && this.settings.audioprocessing && this.lastAudio && this.lastAudio.silent <= 0 && @@ -454,6 +459,8 @@ export class WEAS extends CComponent { */ private updateCanvas() { // update "raw" canvas + if(!this.initialized) + return; // clear the intersection this.context1.clearRect(0, 0, this.canvas1.width, this.canvas1.height); diff --git a/src/weas/index.ts b/src/weas/index.ts index d505a09..2f32d11 100644 --- a/src/weas/index.ts +++ b/src/weas/index.ts @@ -2,11 +2,11 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * */ -export * from "./WEAS"; export * from "./Bea"; +export * from "./WEAS"; \ No newline at end of file diff --git a/src/worker-loader.d.ts b/src/worker-loader.d.ts new file mode 100644 index 0000000..bbcf037 --- /dev/null +++ b/src/worker-loader.d.ts @@ -0,0 +1,29 @@ +/** + * @author hexxone / https://hexx.one + * + * @license + * Copyright (c) 2022 hexxone All rights reserved. + * Licensed under the GNU GENERAL PUBLIC LICENSE. + * See LICENSE file in the project root for full license information. + */ + +/** + * This is just a definition wrapper. + * + * @see https://github.com/webpack-contrib/worker-loader + */ +declare module "worker-loader!*" { + /** + * You need to change `Worker`, if: + * you specified a different value for the `workerType` option + */ + class WebpackWorker extends Worker { + constructor(options?: any); + } + + /** + * Change this if you set the `esModule` option to `false` + * // export = WebpackWorker; + */ + export default WebpackWorker; +} diff --git a/src/worker-loader.d.txt b/src/worker-loader.d.txt deleted file mode 100644 index e55d2e5..0000000 --- a/src/worker-loader.d.txt +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @author hexxone / https://hexx.one - * - * @license - * Copyright (c) 2021 hexxone All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - */ - -/** - * This is just a definition wrapper. - * - * @see https://github.com/webpack-contrib/worker-loader - */ -declare module "worker-loader!*" { - /** - * You need to change `Worker`, if: - * you specified a different value for the `workerType` option - */ - class WebpackWorker { - constructor(options?: any); - onmessage: ((ev: MessageEvent) => any) | null; - onmessageerror: (( ev: MessageEvent) => any) | null; - /** Clones message and transmits it to worker's global environment. transfer can be passed as a list of objects that are to be transferred rather than cloned. */ - postMessage(message: any, transfer: Transferable[]): void; - postMessage(message: any, options?: StructuredSerializeOptions): void; - /** Aborts worker's associated global environment. */ - terminate(): void; - addEventListener(type: K, listener: (ev: WorkerEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; - removeEventListener(type: K, listener: ( ev: WorkerEventMap[K]) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; - } - - /** - * Change this if you set the `esModule` option to `false` - * // export = WebpackWorker; - */ - export default WebpackWorker; -} From ace007c8c2bb740656cc5bd53b2ca29396ad5c21 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 8 Oct 2022 00:37:30 +0200 Subject: [PATCH 57/76] update submodule --- src/three.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/three.ts b/src/three.ts index e9cfd1c..06618da 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit e9cfd1c0459d1898125bce98b5143691d037c31c +Subproject commit 06618da69086c3d72ac8f41de879a8417a7ab32f From ff80b38dddad9b9b4bdf166e5768aea60b1ad19c Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 8 Oct 2022 13:19:10 +0200 Subject: [PATCH 58/76] feat: webglSupported --- README.md | 4 ++++ src/Util.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/README.md b/README.md index a7eb35b..c4809f9 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,10 @@ Keeping track of this stuff manually is annoying... ## [Documentation](https://hexxone.github.io/we_utils) +## TODO + +- fix RenamerPlugin in Prod (Terser Compression) + ### Dependencies / Libraries - [TypeScript](https://www.typescriptlang.org/) for typization diff --git a/src/Util.ts b/src/Util.ts index fa0bb1f..a7b83f9 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -115,3 +115,16 @@ export function sharedWorker( if (iso) bldr = bldr.replace(".wasm", ".shared.wasm"); return wascWorker(bldr, memory, iso, options, useWorker); } + +/** + * Checks whether webgl is available in the current browser + * @returns {boolean} + */ +export function webglSupported() { + try { + const cvs = document.createElement('canvas'); + return !!window.WebGLRenderingContext && (cvs.getContext('webgl') || cvs.getContext('experimental-webgl')); + } catch (e) { + return false; + } +}; \ No newline at end of file From ecc4096d2bd71d231cd25b8286b9b113cc447ed7 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 15 Oct 2022 20:01:59 +0200 Subject: [PATCH 59/76] fix Imports --- src/Util.ts | 2 +- src/offline/OfflineHelper.ts | 2 ++ src/renamer/RenamerPlugin.js | 6 ++++++ src/three.ts | 2 +- src/three/EffectComposer.ts | 18 +++++++--------- src/three/pass/BasePass.ts | 3 ++- src/three/pass/FullScreenHelper.ts | 16 +++++++------- src/three/pass/RenderPass.ts | 15 ++++++------- src/three/pass/ShaderPass.ts | 13 +++++------- src/three/pass/UnrealBloomPass.ts | 22 ++++++++------------ src/three/shader/BlurShader.ts | 3 +-- src/three/shader/ChromaticShader.ts | 3 +-- src/three/shader/FXAAShader.ts | 2 +- src/three/shader/FractalMirrorShader.ts | 2 +- src/three/shader/LuminosityHighPassShader.ts | 3 +-- src/wasc-worker | 2 +- 16 files changed, 52 insertions(+), 62 deletions(-) diff --git a/src/Util.ts b/src/Util.ts index a7b83f9..2d2fd35 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -9,7 +9,7 @@ * @ignore */ -import { wascWorker } from "./wasc-worker"; +import { wascWorker } from "./wasc-worker/Wasc"; import { WascInterface } from "./wasc-worker/WascInterface"; // promise resolve queue diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index 988fecc..06f9577 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -39,6 +39,8 @@ function DontRemove() { return new OfflineWorker(); } +// TODO check if offlinefiles exist before registering + /** * @description * In order to intercept ALL fetch-requests offline, the scope "/" (root) is required. diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js index 0fa6abd..51f0473 100644 --- a/src/renamer/RenamerPlugin.js +++ b/src/renamer/RenamerPlugin.js @@ -115,6 +115,12 @@ class RenamerPlugin { * @return {string} res */ shortenAccessors(pd) { + if(typeof pd !== "string") { + console.error(`Data is not string: '${typeof pd}'`); + return pd; + } + + // 杀 屠 大 门 安 天 const candids = this.shuffle( "職 識 辦 辯 色 特 持 谁 准 彩 就 是 空 虚 纸 张 图 片 末 未 已 己 土 士 干 千 人 入".split( diff --git a/src/three.ts b/src/three.ts index 06618da..e9d9cdb 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 06618da69086c3d72ac8f41de879a8417a7ab32f +Subproject commit e9d9cdb7986c589928ea96a1af149400250ed1c1 diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts index 3130c19..e13b172 100644 --- a/src/three/EffectComposer.ts +++ b/src/three/EffectComposer.ts @@ -4,17 +4,13 @@ * @author hexxone / https://hexx.one */ -import { - LinearFilter, - PerspectiveCamera, - Quaternion, - RGBAFormat, - Scene, - Vector2, - WebGLRenderer, - WebGLRenderTarget, -} from "three.ts/src/"; - +import { PerspectiveCamera } from "../three.ts/src/cameras/PerspectiveCamera"; +import { LinearFilter, RGBAFormat } from "../three.ts/src/constants"; +import { Quaternion } from "../three.ts/src/math/Quaternion"; +import { Vector2 } from "../three.ts/src/math/Vector2"; +import { WebGLRenderer } from "../three.ts/src/renderers/WebGLRenderer"; +import { WebGLRenderTarget } from "../three.ts/src/renderers/WebGLRenderTarget"; +import { Scene } from "../three.ts/src/scenes/Scene"; import { BasePass } from "./pass/BasePass"; import { RenderPass } from "./pass/RenderPass"; diff --git a/src/three/pass/BasePass.ts b/src/three/pass/BasePass.ts index cb1878e..569e0dc 100644 --- a/src/three/pass/BasePass.ts +++ b/src/three/pass/BasePass.ts @@ -5,7 +5,8 @@ * Basic shader pass interface */ -import { WebGLRenderer, WebGLRenderTarget } from "three.ts/src/"; +import { WebGLRenderer } from "three.ts/src/renderers/WebGLRenderer"; +import { WebGLRenderTarget } from "three.ts/src/renderers/WebGLRenderTarget"; export type BasePass = { // child name diff --git a/src/three/pass/FullScreenHelper.ts b/src/three/pass/FullScreenHelper.ts index 23ef971..3aa910d 100644 --- a/src/three/pass/FullScreenHelper.ts +++ b/src/three/pass/FullScreenHelper.ts @@ -4,15 +4,13 @@ * @author hexxone / https://hexx.one */ -import { - Camera, - BufferGeometry, - Mesh, - Material, - OrthographicCamera, - PlaneBufferGeometry, - WebGLRenderer, -} from "three.ts/src/"; +import { Camera } from "three.ts/src/cameras/Camera"; +import { OrthographicCamera } from "three.ts/src/cameras/OrthographicCamera"; +import { BufferGeometry } from "three.ts/src/core/BufferGeometry"; +import { PlaneBufferGeometry } from "three.ts/src/geometries/PlaneGeometry"; +import { Material } from "three.ts/src/materials/Material"; +import { Mesh } from "three.ts/src/objects/Mesh"; +import { WebGLRenderer } from "three.ts/src/renderers/WebGLRenderer"; /** * Helper for passes that need to fill the viewport with a single quad. diff --git a/src/three/pass/RenderPass.ts b/src/three/pass/RenderPass.ts index 0a298b8..4786306 100644 --- a/src/three/pass/RenderPass.ts +++ b/src/three/pass/RenderPass.ts @@ -4,15 +4,12 @@ * @author hexxone / https://hexx.one */ -import { - Camera, - Color, - Material, - Scene, - WebGLRenderer, - WebGLRenderTarget, -} from "three.ts/src/"; - +import { Camera } from "three.ts/src/cameras/Camera"; +import { Material } from "three.ts/src/materials/Material"; +import { Color } from "three.ts/src/math/Color"; +import { WebGLRenderer } from "three.ts/src/renderers/WebGLRenderer"; +import { WebGLRenderTarget } from "three.ts/src/renderers/WebGLRenderTarget"; +import { Scene } from "three.ts/src/scenes/Scene"; import { BasePass } from "./BasePass"; /** diff --git a/src/three/pass/ShaderPass.ts b/src/three/pass/ShaderPass.ts index 397316f..fd86b0e 100644 --- a/src/three/pass/ShaderPass.ts +++ b/src/three/pass/ShaderPass.ts @@ -4,14 +4,11 @@ * @author hexxone / https://hexx.one */ -import { - ShaderMaterial, - UniformsUtils, - Vector2, - WebGLRenderer, - WebGLRenderTarget, -} from "three.ts/src/"; - +import { ShaderMaterial } from "three.ts/src/materials/ShaderMaterial"; +import { Vector2 } from "three.ts/src/math/Vector2"; +import { UniformsUtils } from "three.ts/src/renderers/shaders/UniformsUtils"; +import { WebGLRenderer } from "three.ts/src/renderers/WebGLRenderer"; +import { WebGLRenderTarget } from "three.ts/src/renderers/WebGLRenderTarget"; import { BaseShader } from "../shader/BaseShader"; import { BasePass } from "./BasePass"; diff --git a/src/three/pass/UnrealBloomPass.ts b/src/three/pass/UnrealBloomPass.ts index 662a5e8..872c623 100644 --- a/src/three/pass/UnrealBloomPass.ts +++ b/src/three/pass/UnrealBloomPass.ts @@ -2,20 +2,16 @@ * @author spidersharma / http://eduperiment.com/ */ -import { - AdditiveBlending, - Color, - LinearFilter, - MeshBasicMaterial, - RGBAFormat, - ShaderMaterial, - UniformsUtils, - Vector2, - Vector3, - WebGLRenderer, - WebGLRenderTarget, -} from "three.ts/src/"; +import { AdditiveBlending, LinearFilter, RGBAFormat } from "three.ts/src/constants"; +import { MeshBasicMaterial } from "three.ts/src/materials/MeshBasicMaterial"; +import { ShaderMaterial } from "three.ts/src/materials/ShaderMaterial"; +import { Color } from "three.ts/src/math/Color"; +import { Vector2 } from "three.ts/src/math/Vector2"; +import { Vector3 } from "three.ts/src/math/Vector3"; +import { UniformsUtils } from "three.ts/src/renderers/shaders/UniformsUtils"; +import { WebGLRenderer } from "three.ts/src/renderers/WebGLRenderer"; +import { WebGLRenderTarget } from "three.ts/src/renderers/WebGLRenderTarget"; import { CopyShader } from "../shader/CopyShader"; import { LuminosityHighPassShader } from "../shader/LuminosityHighPassShader"; diff --git a/src/three/shader/BlurShader.ts b/src/three/shader/BlurShader.ts index c3e532b..36a0a15 100644 --- a/src/three/shader/BlurShader.ts +++ b/src/three/shader/BlurShader.ts @@ -7,12 +7,11 @@ * See LICENSE file in the project root for full license information. */ -import { Vector2 } from "three.ts/src/"; - import { BaseShader } from "./BaseShader"; import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/Blur.glsl"; +import { Vector2 } from "three.ts/src/math/Vector2"; /** * Blur shader with Alpha support diff --git a/src/three/shader/ChromaticShader.ts b/src/three/shader/ChromaticShader.ts index 69ce30a..03f0df2 100644 --- a/src/three/shader/ChromaticShader.ts +++ b/src/three/shader/ChromaticShader.ts @@ -7,12 +7,11 @@ * See LICENSE file in the project root for full license information. */ -import { Vector2 } from "three.ts/src/"; - import { BaseShader } from "./BaseShader"; import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/Chromatic.glsl"; +import { Vector2 } from "three.ts/src/math/Vector2"; /** * Chromatic Abberation shader with alpha support diff --git a/src/three/shader/FXAAShader.ts b/src/three/shader/FXAAShader.ts index 073ff3a..6f44cf9 100644 --- a/src/three/shader/FXAAShader.ts +++ b/src/three/shader/FXAAShader.ts @@ -4,7 +4,7 @@ * @author hexxone / https://hexx.one */ -import { Vector2 } from "three.ts/src/"; +import { Vector2 } from "three.ts/src/math/Vector2"; import { BaseShader } from "./BaseShader"; diff --git a/src/three/shader/FractalMirrorShader.ts b/src/three/shader/FractalMirrorShader.ts index 44539a1..b650863 100644 --- a/src/three/shader/FractalMirrorShader.ts +++ b/src/three/shader/FractalMirrorShader.ts @@ -9,7 +9,7 @@ * @description */ -import { Vector2 } from "three.ts/src/"; +import { Vector2 } from "three.ts/src/math/Vector2"; import { BaseShader } from "./BaseShader"; diff --git a/src/three/shader/LuminosityHighPassShader.ts b/src/three/shader/LuminosityHighPassShader.ts index fd0c45b..3654e37 100644 --- a/src/three/shader/LuminosityHighPassShader.ts +++ b/src/three/shader/LuminosityHighPassShader.ts @@ -8,12 +8,11 @@ * See LICENSE file in the project root for full license information. */ -import { Color } from "three.ts/src/"; - import { BaseShader } from "./BaseShader"; import vertex from "./vertex/Basic.glsl"; import fragment from "./fragment/Luminosity.glsl"; +import { Color } from "three.ts/src/math/Color"; /** * Luminosity diff --git a/src/wasc-worker b/src/wasc-worker index fc4e600..d2e1f94 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit fc4e6007fd89463898ca10f0d4ac40caee59d6c0 +Subproject commit d2e1f94c19b1deed6a4f9aaefbcad3b61a8b91f0 From cdf3d70209f19341f2b1980542ebe3209690f53c Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Tue, 25 Oct 2022 23:12:36 +0200 Subject: [PATCH 60/76] feat: working particles --- src/three.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/three.ts b/src/three.ts index e9d9cdb..3fef272 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit e9d9cdb7986c589928ea96a1af149400250ed1c1 +Subproject commit 3fef2727dca587b34882d69b2c724da13f56a909 From b732edb3a6ec65636ba0d47a75fb456a0ddf1bb4 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 5 Nov 2022 09:26:32 +0100 Subject: [PATCH 61/76] fix: load worker --- README.md | 1 - src/XRHelper.ts | 16 ++++++++-------- src/weas/WEAS.ts | 13 ++++++++++--- src/weas/WEAS.wasm.asc | 2 +- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c4809f9..0abf716 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,6 @@ Keeping track of this stuff manually is annoying... - WarnHelper for Seizure warnings - Wallpaper Engine iCUE Library (WEICUE) - Wallpaper Engine Wallpaper Adapter (WEWWA) -- Worker-Loader definition file - Stats.js definition file ### Used by diff --git a/src/XRHelper.ts b/src/XRHelper.ts index 4fab226..6aff661 100644 --- a/src/XRHelper.ts +++ b/src/XRHelper.ts @@ -26,9 +26,9 @@ export class XRSettings extends CSettings { export class XRHelper extends CComponent { public settings: XRSettings = new XRSettings(); - private nav: Navigator; - private button: HTMLButtonElement; - private currentSession: XRSession; + private nav!: Navigator; + private button!: HTMLButtonElement; + private currentSession: XRSession | undefined; /** * Get typed navigator @@ -80,7 +80,7 @@ export class XRHelper extends CComponent { */ private async isSupported() { if ("xr" in this.nav) { - return await this.nav.xr.isSessionSupported("immersive-vr"); + return await this.nav.xr?.isSessionSupported("immersive-vr"); } return false; } @@ -92,7 +92,7 @@ export class XRHelper extends CComponent { * @return {Promise} success if VR is available */ public async enableSession( - sessionCallback: (xrs: XRSession) => void + sessionCallback: (xrs: XRSession | null) => void ): Promise { return new Promise((resolve) => // check availability @@ -121,13 +121,13 @@ export class XRHelper extends CComponent { const sessionInit: XRSessionInit = { optionalFeatures: ["local-floor"], }; - this.nav.xr.requestSession("immersive-vr", sessionInit).then( + this.nav.xr?.requestSession("immersive-vr", sessionInit).then( (sess) => { this.currentSession = sess; const lstnr = (/* event*/) => { - this.currentSession.removeEventListener("end", lstnr); - this.currentSession = null; + this.currentSession?.removeEventListener("end", lstnr); + this.currentSession = undefined; this.button.textContent = "Enter VR"; sessionCallback(null); }; diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 0ccfef1..41979be 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -128,7 +128,7 @@ const PropIDs = { */ export class WEAS extends CComponent { /** @public last processed audio object */ - public lastAudio: WEAudio = null; + public lastAudio: WEAudio | undefined; /** @public settings object */ public settings: WEASettings = new WEASettings(); @@ -168,8 +168,8 @@ export class WEAS extends CComponent { /** * convert float based audio to int - * @param {Array} data - * @return {Array} + * @param {Array} data as + * @return {Array} asd */ private convertAudio(data) { const stereo = []; @@ -184,6 +184,7 @@ export class WEAS extends CComponent { * initializes audio processing pipeline * and starts listening on audio data * @ignore + * @returns {void} */ private async realInit() { // only listen if wallpaper engine context given @@ -220,6 +221,7 @@ export class WEAS extends CComponent { /** * Registers the wallpaper engine audio listener * @ignore + * @returns {void} */ private registerListener() { // register audio callback on module @@ -306,6 +308,7 @@ export class WEAS extends CComponent { /** * Inject preview CSS * @ignore + * @returns {void} */ private injectCSS() { const st = document.createElement("style"); @@ -334,6 +337,7 @@ export class WEAS extends CComponent { /** * Inject preview canvas * @ignore + * @returns {void} */ private injectHTML() { this.mainElm = document.createElement("div"); @@ -369,6 +373,7 @@ export class WEAS extends CComponent { * @param {ArrayLike} dProps processed properties * @return {any} * @ignore + * @returns {void} */ private getProps(dProps: ArrayLike) { const keys = Object.keys(PropIDs); @@ -456,6 +461,7 @@ export class WEAS extends CComponent { /** * Update the debug canvas + * @returns {void} */ private updateCanvas() { // update "raw" canvas @@ -521,6 +527,7 @@ export class WEAS extends CComponent { * Draww Context2 line * @param {string} style * @param {number} y + * @returns {void} */ private drawHorizontLine(style, y) { const ctx = this.context2; diff --git a/src/weas/WEAS.wasm.asc b/src/weas/WEAS.wasm.asc index ee6a9f0..e2b61dd 100644 --- a/src/weas/WEAS.wasm.asc +++ b/src/weas/WEAS.wasm.asc @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2021 hexxone All rights reserved. + * Copyright (c) 2022 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * From 998c92456fe981be932d225d80b21fa2ff5b5d23 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 5 Nov 2022 09:26:49 +0100 Subject: [PATCH 62/76] fix: deps --- src/offline/OfflineHelper.ts | 4 ++-- src/three.ts | 2 +- src/wasc-worker | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index 06f9577..5cc4db3 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -52,7 +52,7 @@ function DontRemove() { * * @public * @param {string} name - the name of the worker - * @param {string} worker - the path to the worker + * @param {string} worker - the Url to instanitate * @param {string} oFile - the path to the offline json file * @return {Promise} finished */ @@ -69,7 +69,7 @@ function register( .then( () => Smallog.info("service-worker registration complete.", oh), (reason) => - Smallog.error("service-worker registration failur: " + reason, oh) + Smallog.error("service-worker registration failure: " + reason, oh) ) .then(() => resolve(true)); return true; diff --git a/src/three.ts b/src/three.ts index 3fef272..4525262 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 3fef2727dca587b34882d69b2c724da13f56a909 +Subproject commit 4525262cb544045cb496751b0ef22d839146dc1a diff --git a/src/wasc-worker b/src/wasc-worker index d2e1f94..cbbf584 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit d2e1f94c19b1deed6a4f9aaefbcad3b61a8b91f0 +Subproject commit cbbf584c5dd2a6893a632557702549cca68b0b12 From 45dc5acf39f13d0c09e99882e990f440875627dd Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sun, 6 Nov 2022 17:03:53 +0100 Subject: [PATCH 63/76] submodule: add worker-loader-fork --- .gitmodules | 3 +++ src/worker-loader-fork | 1 + 2 files changed, 4 insertions(+) create mode 160000 src/worker-loader-fork diff --git a/.gitmodules b/.gitmodules index 905929c..7a244d2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "src/three.ts"] path = src/three.ts url = https://github.com/hexxone/three.ts +[submodule "src/worker-loader-fork"] + path = src/worker-loader-fork + url = https://github.com/hexxone/worker-loader-fork.git diff --git a/src/worker-loader-fork b/src/worker-loader-fork new file mode 160000 index 0000000..733a76b --- /dev/null +++ b/src/worker-loader-fork @@ -0,0 +1 @@ +Subproject commit 733a76bf00ea4e024fea38910238ac686dd15d0d From 8a65e3c7237dd7d2ab62be9216abc8ed28f37fcb Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sun, 6 Nov 2022 21:19:36 +0100 Subject: [PATCH 64/76] chore: submodules --- src/WEWA.ts | 38 +++++++++++--------- src/offline/Offline.worker.ts | 66 ++++++++++++++++------------------- src/wasc-worker | 2 +- src/worker-loader-fork | 2 +- 4 files changed, 53 insertions(+), 55 deletions(-) diff --git a/src/WEWA.ts b/src/WEWA.ts index 0428ad1..f3d10e6 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -17,21 +17,21 @@ const DefLang = "de-de"; /** * WEWWA - * + * * Wallpaper Engine Web Wallpaper Adapter - * + * * This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine * Web-Wallpaper project - so you can test, run & configure it from a normal web browser. - * + * * REQUIREMENTS: - * + * * - HTML5 Browser * - the "project.json" needs to be in the root folder like "index.html" * - this file needs to be included/built in your "index.html" - * - * + * + * * FEATURES: - * + * * - automatically detecting if the web wallpaper is opened by wallpaper engine or browser * - if opened by wallpaper engine, nothing will happen * - if opened by a browser: @@ -57,7 +57,6 @@ const DefLang = "de-de"; * @public */ export class WEWWA { - private audListener: string; private propListener: string; @@ -83,14 +82,19 @@ export class WEWWA { /** * Check if we are running in Web-Mode * if yes => iniitialize, else => do nothing + * @param {string} audListener to register + * @param {string} propListener to register + * @param {string} projFile to register + * @param {string} defLang default languague from project file * @param {Function} finished Callback for initializing the wallpaper */ constructor( audListener = "wallpaperRegisterAudioListener", propListener = "wallpaperPropertyListener", - projFile = "project.json", defLang = "de-de", - finished?: () => {} | null) { - + projFile = "project.json", + defLang = "en-us", + finished?: () => void + ) { this.audListener = audListener; this.propListener = propListener; this.projectFile = projFile; @@ -98,7 +102,7 @@ export class WEWWA { if (window[audListener]) { Smallog.info("detected wallpaper engine => Standby.", LogHead); - finished(); + if (finished !== undefined) finished(); return; } @@ -705,7 +709,7 @@ export class WEWWA { link.setAttribute( "href", "https://steamcommunity.com/sharedfiles/filedetails/?id=" + - proj.workshopid + proj.workshopid ); link.setAttribute("target", "_blank"); link.innerHTML = "

      Open Workshop Page

      "; @@ -1059,10 +1063,10 @@ export class WEWWA { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? [ - parseInt(result[1], 16) / 255, - parseInt(result[2], 16) / 255, - parseInt(result[3], 16) / 255, - ].join(" ") + parseInt(result[1], 16) / 255, + parseInt(result[2], 16) / 255, + parseInt(result[3], 16) / 255, + ].join(" ") : null; } diff --git a/src/offline/Offline.worker.ts b/src/offline/Offline.worker.ts index cb3c8b9..6447318 100644 --- a/src/offline/Offline.worker.ts +++ b/src/offline/Offline.worker.ts @@ -43,14 +43,14 @@ wrk.addEventListener("install", function (event: any) { // let's request the files to store in cache fetch(jsonPath) .then((response) => response.json()) - // after we received the files to store, we open the cache + // after we received the files to store, we open the cache .then((data) => caches .open(wName + version) - // then map the array and add one-by-one + // then map the array and add one-by-one .then((cache) => data.map((url) => cache.add(url))) ) - // log success + // log success .then(() => console.info(wName + " install completed")) ); }); @@ -78,10 +78,7 @@ wrk.addEventListener("fetch", function (event: any) { caches.match(event.request).then(async (cached) => { // return immediately if cache successfull if (cached) { - console.info( - wName + " fetch event(cached): ", - event.request.url - ); + console.info(wName + " fetch event(cached): ", event.request.url); return cached; } @@ -90,10 +87,10 @@ wrk.addEventListener("fetch", function (event: any) { if (preload) return preload; /** - * We copy the response before replying to the network request. - * @param {Response} response This will be stored on the ServiceWorker cache. - * @return {Response} cached - */ + * We copy the response before replying to the network request. + * @param {Response} response This will be stored on the ServiceWorker cache. + * @return {Response} cached + */ function fetchedFromNetwork(response: Response) { const cacheCopy = response.clone(); console.info( @@ -115,26 +112,26 @@ wrk.addEventListener("fetch", function (event: any) { } /** - * When this method is called, it means we were unable to produce a response - * from either the cache or the network. This is our last opportunity - * to produce a meaningful response even when all else fails. - * E.g. - * - Test the Accept header and then return one of the `offlineFundamentals` - * e.g: `return caches.match('/some/cached/image.png')` - * - consider the origin. It's easier to decide what "unavailable" means - * for requests against your origins than for requests against a third party, - * such as an ad provider. - * - Generate a Response programmaticaly, as shown below, and return that. - * @return {Response} fallback - */ + * When this method is called, it means we were unable to produce a response + * from either the cache or the network. This is our last opportunity + * to produce a meaningful response even when all else fails. + * E.g. + * - Test the Accept header and then return one of the `offlineFundamentals` + * e.g: `return caches.match('/some/cached/image.png')` + * - consider the origin. It's easier to decide what "unavailable" means + * for requests against your origins than for requests against a third party, + * such as an ad provider. + * - Generate a Response programmaticaly, as shown below, and return that. + * @return {Response} fallback + */ function unableToResolve() { console.info( wName + " fetch request failed in both cache and network." ); return new Response("Service Unavailable", { - "status": 503, - "statusText": "Service Unavailable", - "headers": new Headers({ + status: 503, + statusText: "Service Unavailable", + headers: new Headers({ "Content-Type": "text/html", }), }); @@ -143,16 +140,13 @@ wrk.addEventListener("fetch", function (event: any) { // fallback to fetching from network return ( fetch(event.request) - // We handle the network request with success and failure scenarios. + // We handle the network request with success and failure scenarios. .then(fetchedFromNetwork, unableToResolve) - // we are done + // we are done .then(() => - console.info( - wName + " fetch event(networked): ", - event.request.url - ) + console.info(wName + " fetch event(networked): ", event.request.url) ) - // We should catch errors on the fetchedFromNetwork handler as well. + // We should catch errors on the fetchedFromNetwork handler as well. .catch(unableToResolve) ); }) @@ -177,17 +171,17 @@ wrk.addEventListener("activate", function (event: any) { // This method will resolve an array of available cache keys. caches .keys() - // We return a promise that settles when all outdated caches are deleted. + // We return a promise that settles when all outdated caches are deleted. .then((keys) => Promise.all( // Filter by keys that don't start with the latest version prefix. keys .filter((key) => !key.endsWith(version)) - // Return a promise that's fulfilled when each outdated cache is deleted. + // Return a promise that's fulfilled when each outdated cache is deleted. .map((key) => caches.delete(key)) ) ) - // completed + // completed .then(() => console.info(wName + " activate completed.")); }); diff --git a/src/wasc-worker b/src/wasc-worker index cbbf584..6112364 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit cbbf584c5dd2a6893a632557702549cca68b0b12 +Subproject commit 6112364d68ce0ae5a0c9763be94092accb5eab6c diff --git a/src/worker-loader-fork b/src/worker-loader-fork index 733a76b..35e10de 160000 --- a/src/worker-loader-fork +++ b/src/worker-loader-fork @@ -1 +1 @@ -Subproject commit 733a76bf00ea4e024fea38910238ac686dd15d0d +Subproject commit 35e10deac2ebcb056dc00ea6746ea187809aacf0 From 32df21efc601ba6f9ecc4152d8c5d2a5d14da1ba Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sun, 6 Nov 2022 21:37:50 +0100 Subject: [PATCH 65/76] update submodules --- src/three.ts | 2 +- src/wasc-worker | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/three.ts b/src/three.ts index 4525262..6d9ea73 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 4525262cb544045cb496751b0ef22d839146dc1a +Subproject commit 6d9ea732baad1e473bdfcf2043f2803e869da437 diff --git a/src/wasc-worker b/src/wasc-worker index 6112364..b8e14a8 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 6112364d68ce0ae5a0c9763be94092accb5eab6c +Subproject commit b8e14a81015983b6001a5edf473485e7fa99d310 From 831c765b1f95e14e8393e03408231e44a4dab894 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sun, 6 Nov 2022 21:58:28 +0100 Subject: [PATCH 66/76] update sub-module --- src/worker-loader-fork | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/worker-loader-fork b/src/worker-loader-fork index 35e10de..83cd7fe 160000 --- a/src/worker-loader-fork +++ b/src/worker-loader-fork @@ -1 +1 @@ -Subproject commit 35e10deac2ebcb056dc00ea6746ea187809aacf0 +Subproject commit 83cd7fe32ceb8545472ec09f034dd35d1e212d3c From 051dc664b31d69ed6763063a20b84ab64f402b41 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 11 Mar 2023 11:46:41 +0100 Subject: [PATCH 67/76] feat: fix mobile window size --- src/Util.ts | 13 ++++++++++++- src/WEICUE.ts | 13 ++++++------- src/index.ts | 3 ++- src/offline/index.ts | 1 + src/weas/WEAS.ts | 10 +++++++--- 5 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 src/offline/index.ts diff --git a/src/Util.ts b/src/Util.ts index 2d2fd35..6e35a32 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -127,4 +127,15 @@ export function webglSupported() { } catch (e) { return false; } -}; \ No newline at end of file +}; + +/** + * Tries to get the correct inner Window Size (for mobile browser mainly). + * This is needed because some mobile browsers include the nav-bar size in the "innerHeight" prop... + * @returns Tuple x,y + */ +export function getRealWindowSize(): { x: number; y: number } { + const realHeight = document.documentElement.clientHeight ?? window.innerHeight; + const realWidth = document.documentElement.clientWidth ?? window.innerWidth; + return { x: realWidth, y: realHeight }; +} diff --git a/src/WEICUE.ts b/src/WEICUE.ts index 7d67e2f..ce0df92 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -11,7 +11,7 @@ import { CComponent } from "./CComponent"; import { CSettings } from "./CSettings"; import { ICUE } from "./ICUE"; import { Smallog } from "./Smallog"; -import { rgbToObj, waitReady } from "./Util"; +import { getRealWindowSize, rgbToObj, waitReady } from "./Util"; import { WEAS } from "./weas/WEAS"; const IMG_SRC = "./img/icue.png"; @@ -210,13 +210,12 @@ export class WEICUE extends CComponent { * @ignore */ private getArea(inPx = false) { + const { x: realWidth, y: realHeight } = getRealWindowSize(); const sett = this.settings; - const wwid = window.innerWidth; - const whei = window.innerHeight; - const w = (wwid * sett.icue_area_width) / 100; - const h = (whei * sett.icue_area_height) / 100; - const l = ((wwid - w) * sett.icue_area_xoff) / 100; - const t = ((whei - h) * sett.icue_area_yoff) / 100; + const w = (realWidth * sett.icue_area_width) / 100; + const h = (realHeight * sett.icue_area_height) / 100; + const l = ((realWidth - w) * sett.icue_area_xoff) / 100; + const t = ((realHeight - h) * sett.icue_area_yoff) / 100; return { width: w + (inPx ? "px" : ""), height: h + (inPx ? "px" : ""), diff --git a/src/index.ts b/src/index.ts index 7595e87..646160f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,8 @@ */ // Offline-Worker entry-point -export * from "./offline/OfflineHelper"; +// Not exported specifically because including this index.ts would automatically build the worker. +// export * from "./offline/OfflineHelper"; // custom effects export * from "./three"; diff --git a/src/offline/index.ts b/src/offline/index.ts new file mode 100644 index 0000000..342a842 --- /dev/null +++ b/src/offline/index.ts @@ -0,0 +1 @@ +export * from "./OfflineHelper"; \ No newline at end of file diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 0ccfef1..6afe13f 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -10,7 +10,7 @@ import { CComponent } from "../CComponent"; import { CSettings } from "../CSettings"; import { Smallog } from "../Smallog"; -import { sharedWorker, waitReady } from "../Util"; +import { getRealWindowSize, sharedWorker, waitReady } from "../Util"; import { WascInterface } from "../wasc-worker/WascInterface"; import { Bea_ts } from "./Bea"; @@ -353,12 +353,16 @@ export class WEAS extends CComponent { `; document.body.append(this.mainElm); + + const { x: realWidth, y: realHeight } = getRealWindowSize(); + // TODO should also apply Height ???? + this.canvas1 = document.getElementById("WEASCanvas1") as HTMLCanvasElement; - this.canvas1.width = window.innerWidth; + this.canvas1.width = realWidth; this.context1 = this.canvas1.getContext("2d"); this.canvas2 = document.getElementById("WEASCanvas2") as HTMLCanvasElement; - this.canvas2.width = window.innerWidth; + this.canvas2.width = realWidth; this.context2 = this.canvas2.getContext("2d"); this.display = document.getElementById("WEASDisplay"); From 4ecfd0c4310ebbfc4fa17856571f776b78e8eeea Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 11 Mar 2023 13:35:31 +0100 Subject: [PATCH 68/76] feat: refactoring, Copyright --- src/CComponent.ts | 2 +- src/CSettings.ts | 2 +- src/FPSta.ts | 2 +- src/LoadHelper.ts | 2 +- src/ReloadHelper.ts | 2 +- src/Smallog.ts | 2 +- src/Util.ts | 2 +- src/WEICUE.ts | 2 +- src/WEWA.ts | 2 +- src/WarnHelper.ts | 2 +- src/index.ts | 2 +- src/offline/Offline.worker.ts | 2 +- src/offline/OfflineHelper.ts | 2 +- src/renamer/RenamerPlugin.js | 14 ++++++++++---- src/three/shader/BaseShader.ts | 2 +- src/three/shader/BlendShader.ts | 2 +- src/three/shader/BlurShader.ts | 2 +- src/three/shader/ChromaticShader.ts | 2 +- src/three/shader/CopyShader.ts | 2 +- src/three/shader/FractalMirrorShader.ts | 2 +- src/three/shader/LUTShader.ts | 2 +- src/three/shader/LUTShaderNearest.ts | 2 +- src/three/shader/LuminosityHighPassShader.ts | 2 +- src/wasc-worker | 2 +- src/weas/WEAS.ts | 2 +- src/weas/WEAS.wasm.asc | 2 +- src/weas/index.ts | 2 +- src/worker-loader.d.ts | 2 +- 28 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/CComponent.ts b/src/CComponent.ts index cb11d36..de4addd 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/CSettings.ts b/src/CSettings.ts index c3515b4..bd87761 100644 --- a/src/CSettings.ts +++ b/src/CSettings.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/FPSta.ts b/src/FPSta.ts index 3157150..f5f3d2a 100644 --- a/src/FPSta.ts +++ b/src/FPSta.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/LoadHelper.ts b/src/LoadHelper.ts index c5df575..47007f5 100644 --- a/src/LoadHelper.ts +++ b/src/LoadHelper.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/ReloadHelper.ts b/src/ReloadHelper.ts index 0be5da2..1ed2dc6 100644 --- a/src/ReloadHelper.ts +++ b/src/ReloadHelper.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/Smallog.ts b/src/Smallog.ts index f22c015..ce0bf71 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/Util.ts b/src/Util.ts index 6e35a32..2de1b0d 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/WEICUE.ts b/src/WEICUE.ts index ce0df92..c5fd6fa 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/WEWA.ts b/src/WEWA.ts index f3d10e6..f9ac101 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/WarnHelper.ts b/src/WarnHelper.ts index ad8b76f..334fecd 100644 --- a/src/WarnHelper.ts +++ b/src/WarnHelper.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/index.ts b/src/index.ts index 646160f..3f3b710 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/offline/Offline.worker.ts b/src/offline/Offline.worker.ts index 6447318..6da5988 100644 --- a/src/offline/Offline.worker.ts +++ b/src/offline/Offline.worker.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/offline/OfflineHelper.ts b/src/offline/OfflineHelper.ts index 5cc4db3..40d0375 100644 --- a/src/offline/OfflineHelper.ts +++ b/src/offline/OfflineHelper.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js index 51f0473..0bb2fd5 100644 --- a/src/renamer/RenamerPlugin.js +++ b/src/renamer/RenamerPlugin.js @@ -115,7 +115,7 @@ class RenamerPlugin { * @return {string} res */ shortenAccessors(pd) { - if(typeof pd !== "string") { + if (typeof pd !== "string") { console.error(`Data is not string: '${typeof pd}'`); return pd; } @@ -337,15 +337,21 @@ class RenamerPlugin { let newStrict = ""; const keys = Object.keys(globalMap); if (keys.length > 4) { + // do some small scr1pt-k1ddy pr0tect1on :) const fk1 = lib.JSFuck.encode(rndOff.toString()); const fk2 = mayEncode("split"); const fk3 = lib.JSFuck.encode(delim); const fk4 = mayEncode("length"); const fk6 = lib.JSFuck.encode("atob"); const fk7 = mayEncode("String"); - const tmp1 = "`fromC${Σ}`"; - const tmp2 = "`c${Σ}At`"; - const func = `Ⅾ=>{var Ή=window,Σ='harCode',Ⅹ=${fk4},χ=Ή,Ή='';for(var Ἰ=0,Ⅾ=χ[${fk6}](Ⅾ);Ἰ<Ⅾ[Ⅹ];Ή+=χ[${fk7}][${tmp1}]((Ⅾ[Ἰ++][${tmp2}]())-(${fk1}))){}return Ή[${fk2}](${fk3})}`; + const fk8 = lib.JSFuck.encode("har") + `+Δ+` + lib.JSFuck.encode("ode"); + const fk9 = lib.JSFuck.encode("from"); + const fk10 = lib.JSFuck.encode("At"); + const fk11 = lib.JSFuck.encode("C"); + + const tmp1 = "`${" + fk9 + "}${Δ}${Σ}`"; + const tmp2 = "`c${Σ}${" + fk10 + "}`"; + const func = `Ⅾ=>{var Δ=${fk11},Ή=window,Σ=${fk8},Ⅹ=${fk4},χ=Ή,one='6765742072336b74206b6964646f',Ή=[]+[],α=Ή;for(var Ἰ=0,Ⅾ=χ[${fk6}](Ⅾ);Ἰ<Ⅾ[Ⅹ];Ή+=χ[${fk7}][${tmp1}]((Ⅾ[Ἰ++][${tmp2}]())-(${fk1}))){}return Ή[${fk2}](${fk3})}`; newStrict += `var ${splFunc}=${func},`; } diff --git a/src/three/shader/BaseShader.ts b/src/three/shader/BaseShader.ts index 1d62bc3..8f08a2b 100644 --- a/src/three/shader/BaseShader.ts +++ b/src/three/shader/BaseShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license -* Copyright (c) 2022 hexxone All rights reserved. +* Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/BlendShader.ts b/src/three/shader/BlendShader.ts index 4360fdd..43ffe32 100644 --- a/src/three/shader/BlendShader.ts +++ b/src/three/shader/BlendShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license -* Copyright (c) 2022 hexxone All rights reserved. +* Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/BlurShader.ts b/src/three/shader/BlurShader.ts index 36a0a15..0229c9c 100644 --- a/src/three/shader/BlurShader.ts +++ b/src/three/shader/BlurShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/ChromaticShader.ts b/src/three/shader/ChromaticShader.ts index 03f0df2..7096e71 100644 --- a/src/three/shader/ChromaticShader.ts +++ b/src/three/shader/ChromaticShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/CopyShader.ts b/src/three/shader/CopyShader.ts index 829b3ec..2844e2f 100644 --- a/src/three/shader/CopyShader.ts +++ b/src/three/shader/CopyShader.ts @@ -3,7 +3,7 @@ * @author hexxone / https://hexx.one * * @license -* Copyright (c) 2022 hexxone All rights reserved. +* Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/FractalMirrorShader.ts b/src/three/shader/FractalMirrorShader.ts index b650863..85e483f 100644 --- a/src/three/shader/FractalMirrorShader.ts +++ b/src/three/shader/FractalMirrorShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/three/shader/LUTShader.ts b/src/three/shader/LUTShader.ts index cdcdd9e..a4331e5 100644 --- a/src/three/shader/LUTShader.ts +++ b/src/three/shader/LUTShader.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license -* Copyright (c) 2022 hexxone All rights reserved. +* Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/LUTShaderNearest.ts b/src/three/shader/LUTShaderNearest.ts index 754c87b..ecdb5be 100644 --- a/src/three/shader/LUTShaderNearest.ts +++ b/src/three/shader/LUTShaderNearest.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license -* Copyright (c) 2022 hexxone All rights reserved. +* Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/three/shader/LuminosityHighPassShader.ts b/src/three/shader/LuminosityHighPassShader.ts index 3654e37..b316fac 100644 --- a/src/three/shader/LuminosityHighPassShader.ts +++ b/src/three/shader/LuminosityHighPassShader.ts @@ -3,7 +3,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/wasc-worker b/src/wasc-worker index b8e14a8..2e948cc 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit b8e14a81015983b6001a5edf473485e7fa99d310 +Subproject commit 2e948cc239d859ac6071991384558c687577c63c diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index 3934014..ff302ed 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ diff --git a/src/weas/WEAS.wasm.asc b/src/weas/WEAS.wasm.asc index e2b61dd..3cb5731 100644 --- a/src/weas/WEAS.wasm.asc +++ b/src/weas/WEAS.wasm.asc @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/weas/index.ts b/src/weas/index.ts index 2f32d11..d90a813 100644 --- a/src/weas/index.ts +++ b/src/weas/index.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * diff --git a/src/worker-loader.d.ts b/src/worker-loader.d.ts index bbcf037..d31f93b 100644 --- a/src/worker-loader.d.ts +++ b/src/worker-loader.d.ts @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. */ From 91b9aa5311c3b024d4e026b15239db35bc807d20 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 11 Mar 2023 23:13:44 +0100 Subject: [PATCH 69/76] chore: submodule --- src/three.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/three.ts b/src/three.ts index 6d9ea73..8f92e56 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 6d9ea732baad1e473bdfcf2043f2803e869da437 +Subproject commit 8f92e56d55c9e1d1244c56803f647b213e19d810 From d4220aba5f5d924bcc82264e0f58a1887bcc2201 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sun, 12 Mar 2023 17:51:35 +0100 Subject: [PATCH 70/76] chore: submodule --- src/worker-loader-fork | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/worker-loader-fork b/src/worker-loader-fork index 83cd7fe..f955445 160000 --- a/src/worker-loader-fork +++ b/src/worker-loader-fork @@ -1 +1 @@ -Subproject commit 83cd7fe32ceb8545472ec09f034dd35d1e212d3c +Subproject commit f955445b36344668e71634c3b944eef6855b2b0e From a98f7b34a25a62b3d539a47289c2883718db1a9c Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Thu, 20 Apr 2023 18:54:13 +0200 Subject: [PATCH 71/76] chore: submodule --- src/WEWA.ts | 8 ++++---- src/worker-loader-fork | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/WEWA.ts b/src/WEWA.ts index f9ac101..d5df53d 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -13,7 +13,7 @@ import { waitReady } from "./Util"; import { WascUtil } from "./wasc-worker/WascUtil"; const LogHead = "[WEWWA] "; -const DefLang = "de-de"; +const DefLang = "en-us"; /** * WEWWA @@ -82,18 +82,18 @@ export class WEWWA { /** * Check if we are running in Web-Mode * if yes => iniitialize, else => do nothing + * @param {Function} finished Callback for initializing the wallpaper * @param {string} audListener to register * @param {string} propListener to register * @param {string} projFile to register * @param {string} defLang default languague from project file - * @param {Function} finished Callback for initializing the wallpaper */ constructor( + finished: () => void = undefined, audListener = "wallpaperRegisterAudioListener", propListener = "wallpaperPropertyListener", projFile = "project.json", - defLang = "en-us", - finished?: () => void + defLang = DefLang ) { this.audListener = audListener; this.propListener = propListener; diff --git a/src/worker-loader-fork b/src/worker-loader-fork index f955445..1305f19 160000 --- a/src/worker-loader-fork +++ b/src/worker-loader-fork @@ -1 +1 @@ -Subproject commit f955445b36344668e71634c3b944eef6855b2b0e +Subproject commit 1305f19ffa2dd27ac07db10db254833ba89b34bb From 8a9719d07066c0208e3927bcaa43dbd9fb4f387f Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 22 Apr 2023 21:51:29 +0200 Subject: [PATCH 72/76] chore: RGB; logging; copyright --- src/CComponent.ts | 7 +-- src/Smallog.ts | 4 +- src/Util.ts | 101 ++++++++++++++++++++++++----------- src/XRHelper.ts | 4 +- src/offline/OfflinePlugin.js | 2 +- src/renamer/RenamerPlugin.js | 3 +- src/wasc-worker | 2 +- src/weas/WEAS.ts | 4 +- src/worker-loader-fork | 2 +- 9 files changed, 85 insertions(+), 44 deletions(-) diff --git a/src/CComponent.ts b/src/CComponent.ts index de4addd..0f4a901 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -33,11 +33,11 @@ export class CComponent { * will also flag the module as needs-Update. * * @public - * @param {Object} key - * @param {Object} value + * @param {Object} key key + * @param {Object} value value * @return {boolean} found */ - public applySetting(key: any, value: any): boolean { + public applySetting(key: string, value: any): boolean { let found = this.settings.apply(key, value); if (found) { this.needsUpdate = true; @@ -55,6 +55,7 @@ export class CComponent { * will recursively update all needed modules after settings changes * * @public + * @return {void} */ public updateAll(): void { this.children.forEach((c) => c.updateAll()); diff --git a/src/Smallog.ts b/src/Smallog.ts index ce0bf71..ba42c2a 100644 --- a/src/Smallog.ts +++ b/src/Smallog.ts @@ -36,7 +36,7 @@ export enum LogLevel { * Small logging util, with name/time prefix & log levels */ class Smalog { - logLevel: LogLevel = LogLevel.Debug; // @todo change default logging level + logLevel: LogLevel = LogLevel.Info; // @todo change default logging level preFix = "[Smallog] "; printTime = false; @@ -105,7 +105,7 @@ class Smalog { * @param {string} hdr overwrite header * @return {void} nothing */ - warn(msg: string, hdr: string = this.preFix) { + warn(msg: string, hdr: string = this.preFix) { if (this.logLevel >= 1) { if (this.printTime) msg = "[" + new Date().toLocaleString() + "] " + msg; if (this.logLevel >= 2) hdr = this.traceCall(hdr); diff --git a/src/Util.ts b/src/Util.ts index 2de1b0d..2cb946e 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -42,56 +42,93 @@ export function waitReady() { } /** - * @todo check for rgba errors - * Convert helper - * @param {string} r_g_b format: "r g b" where each is float 0-1 + * Color Convert helper + * Support following input formats: + * - RGB float notation: "r g b" + * - RGBA float notation: "r g b a" + * - RGB hex notation: "#RRGGBB" + * - RGBA hex notation: "#RRGGBBAA" + * - RGB bracket notation: "rgb(r, g, b)" + * - RGBA bracket notation: "rgba(r, g, b, a)" + * @param {string} input format: "r g b a" where each is float 0-1, or hex color notation "#RRGGBBAA", or "rgba(r, g, b, a)" * @param {number} mlt multiplier (default 255) * @return {Object} {r,g,b,a} with float 0-mlt */ export function rgbToObj( - r_g_b: string, + input: string, mlt = 255 ): { r: number; g: number; b: number; a: number } { - // fix support for rgba strings - const brackI = r_g_b.indexOf("("); + const hexRegex = /^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/; + + if (hexRegex.test(input)) { + const hexToDecimal = (hex: string) => parseInt(hex, 16); + const r = hexToDecimal(input.slice(1, 3)); + const g = hexToDecimal(input.slice(3, 5)); + const b = hexToDecimal(input.slice(5, 7)); + const a = input.length === 9 ? hexToDecimal(input.slice(7, 9)) : mlt; + + return { r, g, b, a }; + } + + const brackI = input.indexOf("("); if (brackI > -1) { - r_g_b = r_g_b.substring(brackI + 1, r_g_b.indexOf(")")); + input = input.substring(brackI + 1, input.indexOf(")")); } - // do splitting and multiplying - const spl = r_g_b.split(" ") as any[]; + + const spl = input.split(/[\s,]+/).map(parseFloat); for (let i = 0; i < spl.length; i++) { spl[i] = isNaN(spl[i]) ? 0 : Math.min(mlt, Math.max(0, spl[i] * mlt)); } + + if (spl.length < 4) { + spl[3] = mlt; + } + return { r: spl[0] || 0, g: spl[1] || 0, b: spl[2] || 0, - a: spl[3] || 1 * mlt, + a: spl[3], }; } /** - * Convert helper - * @param {string} r_g_b format: "r g b" where each is float 0-1 + * Color Convert helper + * Support following input formats: + * - RGB float notation: "r g b" + * - RGBA float notation: "r g b a" + * - RGB hex notation: "#RRGGBB" + * - RGBA hex notation: "#RRGGBBAA" + * - RGB bracket notation: "rgb(r, g, b)" + * - RGBA bracket notation: "rgba(r, g, b, a)" + * @param {string} input format: "r g b a" where each is float 0-1, or hex color notation "#RRGGBBAA", or "rgba(r, g, b, a)" * @return {Object} {h,s,l} with float 0-1 */ -export function rgbToHSL(r_g_b: string): { h: number; s: number; l: number } { - const cO = rgbToObj(r_g_b, 1); - const ma = Math.max(cO.r, cO.g, cO.b); - const mi = Math.min(cO.r, cO.g, cO.b); +export function rgbToHSL(input: string): { h: number; s: number; l: number } { + const colorObject = rgbToObj(input, 1); + const r = colorObject.r / 255; + const g = colorObject.g / 255; + const b = colorObject.b / 255; + const ma = Math.max(r, g, b); + const mi = Math.min(r, g, b); const hsl = { h: 0, s: 0, l: (mi + ma) / 2 }; const t = ma - mi; - switch (((hsl.s = hsl.l <= 0.5 ? t / (ma + mi) : t / (2 - ma - mi)), ma)) { - case cO.r: - hsl.h = (cO.g - cO.b) / t + (cO.g < cO.b ? 6 : 0); - break; - case cO.g: - hsl.h = (cO.b - cO.r) / t + 2; - break; - case cO.b: - hsl.h = (cO.r - cO.g) / t + 4; + + if (t !== 0) { + hsl.s = hsl.l <= 0.5 ? t / (ma + mi) : t / (2 - ma - mi); + switch (ma) { + case r: + hsl.h = (g - b) / t + (g < b ? 6 : 0); + break; + case g: + hsl.h = (b - r) / t + 2; + break; + case b: + hsl.h = (r - g) / t + 4; + } + hsl.h /= 6; } - hsl.h /= 6; + return hsl; } @@ -122,12 +159,15 @@ export function sharedWorker( */ export function webglSupported() { try { - const cvs = document.createElement('canvas'); - return !!window.WebGLRenderingContext && (cvs.getContext('webgl') || cvs.getContext('experimental-webgl')); + const cvs = document.createElement("canvas"); + return ( + !!window.WebGLRenderingContext && + (cvs.getContext("webgl") || cvs.getContext("experimental-webgl")) + ); } catch (e) { return false; } -}; +} /** * Tries to get the correct inner Window Size (for mobile browser mainly). @@ -135,7 +175,8 @@ export function webglSupported() { * @returns Tuple x,y */ export function getRealWindowSize(): { x: number; y: number } { - const realHeight = document.documentElement.clientHeight ?? window.innerHeight; + const realHeight = + document.documentElement.clientHeight ?? window.innerHeight; const realWidth = document.documentElement.clientWidth ?? window.innerWidth; return { x: realWidth, y: realHeight }; } diff --git a/src/XRHelper.ts b/src/XRHelper.ts index 6aff661..ada0d60 100644 --- a/src/XRHelper.ts +++ b/src/XRHelper.ts @@ -126,7 +126,9 @@ export class XRHelper extends CComponent { this.currentSession = sess; const lstnr = (/* event*/) => { - this.currentSession?.removeEventListener("end", lstnr); + if (this.currentSession) { + this.currentSession.removeEventListener("end", lstnr); + } this.currentSession = undefined; this.button.textContent = "Enter VR"; sessionCallback(null); diff --git a/src/offline/OfflinePlugin.js b/src/offline/OfflinePlugin.js index 2a5f56a..f0193bc 100644 --- a/src/offline/OfflinePlugin.js +++ b/src/offline/OfflinePlugin.js @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * @ignore diff --git a/src/renamer/RenamerPlugin.js b/src/renamer/RenamerPlugin.js index 0bb2fd5..0c79212 100644 --- a/src/renamer/RenamerPlugin.js +++ b/src/renamer/RenamerPlugin.js @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * @@ -120,7 +120,6 @@ class RenamerPlugin { return pd; } - // 杀 屠 大 门 安 天 const candids = this.shuffle( "職 識 辦 辯 色 特 持 谁 准 彩 就 是 空 虚 纸 张 图 片 末 未 已 己 土 士 干 千 人 入".split( diff --git a/src/wasc-worker b/src/wasc-worker index 2e948cc..9d8a4ad 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 2e948cc239d859ac6071991384558c687577c63c +Subproject commit 9d8a4ad7867fa9c709cc42c023c568ecaa5a9953 diff --git a/src/weas/WEAS.ts b/src/weas/WEAS.ts index ff302ed..fcf6c9e 100644 --- a/src/weas/WEAS.ts +++ b/src/weas/WEAS.ts @@ -357,7 +357,6 @@ export class WEAS extends CComponent { `; document.body.append(this.mainElm); - const { x: realWidth, y: realHeight } = getRealWindowSize(); // TODO should also apply Height ???? @@ -469,8 +468,7 @@ export class WEAS extends CComponent { */ private updateCanvas() { // update "raw" canvas - if(!this.initialized) - return; + if (!this.initialized) return; // clear the intersection this.context1.clearRect(0, 0, this.canvas1.width, this.canvas1.height); diff --git a/src/worker-loader-fork b/src/worker-loader-fork index 1305f19..0a4903f 160000 --- a/src/worker-loader-fork +++ b/src/worker-loader-fork @@ -1 +1 @@ -Subproject commit 1305f19ffa2dd27ac07db10db254833ba89b34bb +Subproject commit 0a4903f9342ae7cacc07e71d59b4e5c4dca152b7 From dc7b3953760f1a8304c399f2ca52e5d89725f613 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 19 Aug 2023 19:24:37 +0200 Subject: [PATCH 73/76] feat: upgrade WEAS to new AssemblyScript --- src/offline/OfflinePlugin.js | 2 +- src/three.ts | 2 +- src/wasc-worker | 2 +- src/weas/{WEAS.wasm.asc => assembly/WEAS.ts} | 0 src/weas/assembly/tsconfig.json | 9 +++++++++ src/worker-loader-fork | 2 +- src/worker-loader.d.ts | 2 +- 7 files changed, 14 insertions(+), 5 deletions(-) rename src/weas/{WEAS.wasm.asc => assembly/WEAS.ts} (100%) create mode 100644 src/weas/assembly/tsconfig.json diff --git a/src/offline/OfflinePlugin.js b/src/offline/OfflinePlugin.js index 2a5f56a..f0193bc 100644 --- a/src/offline/OfflinePlugin.js +++ b/src/offline/OfflinePlugin.js @@ -2,7 +2,7 @@ * @author hexxone / https://hexx.one * * @license - * Copyright (c) 2022 hexxone All rights reserved. + * Copyright (c) 2023 hexxone All rights reserved. * Licensed under the GNU GENERAL PUBLIC LICENSE. * See LICENSE file in the project root for full license information. * @ignore diff --git a/src/three.ts b/src/three.ts index 8f92e56..f5dc233 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit 8f92e56d55c9e1d1244c56803f647b213e19d810 +Subproject commit f5dc23341db58629f1e2e85219c60b0b2a610384 diff --git a/src/wasc-worker b/src/wasc-worker index 2e948cc..0109358 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 2e948cc239d859ac6071991384558c687577c63c +Subproject commit 0109358a2aba12fd46602e9d993cd0b4c160ad78 diff --git a/src/weas/WEAS.wasm.asc b/src/weas/assembly/WEAS.ts similarity index 100% rename from src/weas/WEAS.wasm.asc rename to src/weas/assembly/WEAS.ts diff --git a/src/weas/assembly/tsconfig.json b/src/weas/assembly/tsconfig.json new file mode 100644 index 0000000..165fef9 --- /dev/null +++ b/src/weas/assembly/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": [ + "./**/*.ts" + ], + "compilerOptions": { + "strictBindCallApply": false + } +} \ No newline at end of file diff --git a/src/worker-loader-fork b/src/worker-loader-fork index f955445..0985906 160000 --- a/src/worker-loader-fork +++ b/src/worker-loader-fork @@ -1 +1 @@ -Subproject commit f955445b36344668e71634c3b944eef6855b2b0e +Subproject commit 0985906b8af3b97b5232e2daf11c7b3935d77e15 diff --git a/src/worker-loader.d.ts b/src/worker-loader.d.ts index d31f93b..55e1a29 100644 --- a/src/worker-loader.d.ts +++ b/src/worker-loader.d.ts @@ -26,4 +26,4 @@ declare module "worker-loader!*" { * // export = WebpackWorker; */ export default WebpackWorker; -} +} \ No newline at end of file From 0f8c747eba8d85be32cd7c40e8e580954e3ef20e Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sat, 19 Aug 2023 19:30:04 +0200 Subject: [PATCH 74/76] bruh --- src/wasc-worker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasc-worker b/src/wasc-worker index 0109358..ba1a27f 160000 --- a/src/wasc-worker +++ b/src/wasc-worker @@ -1 +1 @@ -Subproject commit 0109358a2aba12fd46602e9d993cd0b4c160ad78 +Subproject commit ba1a27f9405a483c51d134c555ccb5e9bc142ca0 From 93d210ba561ec300923fef2f5ec56cdb96b72eb1 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Sun, 20 Aug 2023 18:27:36 +0200 Subject: [PATCH 75/76] chore: type refactoring --- src/{ => @types}/gles.d.ts | 0 src/{ => @types}/glsl.d.ts | 0 src/{ICUE.d.ts => @types/we-icue.d.ts} | 0 src/FPSta.ts | 23 ++++++++++++-------- src/three/EffectComposer.ts | 1 + src/worker-loader-fork | 2 +- src/worker-loader.d.ts | 29 -------------------------- 7 files changed, 16 insertions(+), 39 deletions(-) rename src/{ => @types}/gles.d.ts (100%) rename src/{ => @types}/glsl.d.ts (100%) rename src/{ICUE.d.ts => @types/we-icue.d.ts} (100%) delete mode 100644 src/worker-loader.d.ts diff --git a/src/gles.d.ts b/src/@types/gles.d.ts similarity index 100% rename from src/gles.d.ts rename to src/@types/gles.d.ts diff --git a/src/glsl.d.ts b/src/@types/glsl.d.ts similarity index 100% rename from src/glsl.d.ts rename to src/@types/glsl.d.ts diff --git a/src/ICUE.d.ts b/src/@types/we-icue.d.ts similarity index 100% rename from src/ICUE.d.ts rename to src/@types/we-icue.d.ts diff --git a/src/FPSta.ts b/src/FPSta.ts index f5f3d2a..1832ad0 100644 --- a/src/FPSta.ts +++ b/src/FPSta.ts @@ -12,8 +12,8 @@ import { CSettings } from "./CSettings"; import { waitReady } from "./Util"; import { WEAS } from "./weas/WEAS"; -const Element_Id = "fpstats"; -const Mem_Update_Rate = 19; +const ElementId = "fpstats"; +const MemUpdateRate = 19; /** @@ -35,22 +35,27 @@ export class FPStats extends CComponent { public settings: FPSettings = new FPSettings(); private container: HTMLElement; + // FPS private fpsHolder: HTMLElement; private lastUpdate: number = performance.now(); private frameCount = 1; + // usage private useHolder: HTMLElement; + // cpu private cpuHolder: HTMLElement; private cpuBegin: number = performance.now(); private cpuEnd: number = performance.now(); private cpuMS = 1; + // gpu private gpuHolder: HTMLElement; private gpuBegin: number = performance.now(); private gpuEnd: number = performance.now(); private gpuMS = 1; + // audio private auProvider: WEAS = null; private audHolder: HTMLElement; @@ -88,7 +93,7 @@ export class FPStats extends CComponent { private injectCSS() { const st = document.createElement("style"); st.innerHTML = ` - #${Element_Id} { + #${ElementId} { opacity: 0; position: fixed; top: 50vh; @@ -99,7 +104,7 @@ export class FPStats extends CComponent { text-align: left; background: black; } - #${Element_Id}.show { + #${ElementId}.show { opacity: 0.8; } `; @@ -113,17 +118,17 @@ export class FPStats extends CComponent { private injectHTML() { // root this.container = document.createElement("div"); - this.container.id = Element_Id; + this.container.id = ElementId; document.body.append(this.container); // fps this.fpsHolder = document.createElement("div"); - this.fpsHolder.innerText = "FPS: 0"; + this.fpsHolder.innerText = "FPS: 0 / 60"; // cpu this.cpuHolder = document.createElement("div"); - this.cpuHolder.innerText = "CPU: 0.00 ms"; + this.cpuHolder.innerText = "CPU: 0.00%"; // gpu this.gpuHolder = document.createElement("div"); - this.gpuHolder.innerText = "GPU: 0.00 ms"; + this.gpuHolder.innerText = "GPU: 0.00%"; // usage this.useHolder = document.createElement("div"); this.useHolder.innerText = "All: 0.00%"; @@ -231,7 +236,7 @@ export class FPStats extends CComponent { this.bpmHolder.innerText = `BPM: ${bts.toFixed(2)} ~`; } - if (this.memUpdate++ > Mem_Update_Rate) { + if (this.memUpdate++ > MemUpdateRate) { this.memUpdate = 0; this.updateMemory(); } diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts index e13b172..f767d5a 100644 --- a/src/three/EffectComposer.ts +++ b/src/three/EffectComposer.ts @@ -16,6 +16,7 @@ import { RenderPass } from "./pass/RenderPass"; /** * render shader chain + * // TODO move to three.ts ?? * @public */ export class EffectComposer { diff --git a/src/worker-loader-fork b/src/worker-loader-fork index 0985906..208889e 160000 --- a/src/worker-loader-fork +++ b/src/worker-loader-fork @@ -1 +1 @@ -Subproject commit 0985906b8af3b97b5232e2daf11c7b3935d77e15 +Subproject commit 208889e6f1467e25923f0f966bbc12597cf38901 diff --git a/src/worker-loader.d.ts b/src/worker-loader.d.ts deleted file mode 100644 index 55e1a29..0000000 --- a/src/worker-loader.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @author hexxone / https://hexx.one - * - * @license - * Copyright (c) 2023 hexxone All rights reserved. - * Licensed under the GNU GENERAL PUBLIC LICENSE. - * See LICENSE file in the project root for full license information. - */ - -/** - * This is just a definition wrapper. - * - * @see https://github.com/webpack-contrib/worker-loader - */ -declare module "worker-loader!*" { - /** - * You need to change `Worker`, if: - * you specified a different value for the `workerType` option - */ - class WebpackWorker extends Worker { - constructor(options?: any); - } - - /** - * Change this if you set the `esModule` option to `false` - * // export = WebpackWorker; - */ - export default WebpackWorker; -} \ No newline at end of file From 62e05b66fd033a58ba81e7a2f5337e98d45da6e4 Mon Sep 17 00:00:00 2001 From: "hexx.one" Date: Wed, 29 Nov 2023 20:04:48 +0100 Subject: [PATCH 76/76] feat: null to undefined --- src/@types/gles.d.ts | 941 ----------------------------- src/CComponent.ts | 2 +- src/FPSta.ts | 4 +- src/WEICUE.ts | 18 +- src/WEWA.ts | 51 +- src/three.ts | 2 +- src/three/EffectComposer.ts | 4 +- src/three/pass/FullScreenHelper.ts | 8 +- src/three/pass/RenderPass.ts | 28 +- 9 files changed, 59 insertions(+), 999 deletions(-) delete mode 100644 src/@types/gles.d.ts diff --git a/src/@types/gles.d.ts b/src/@types/gles.d.ts deleted file mode 100644 index 440c076..0000000 --- a/src/@types/gles.d.ts +++ /dev/null @@ -1,941 +0,0 @@ -/* eslint-disable @typescript-eslint/consistent-type-definitions */ -/* eslint-disable @typescript-eslint/no-empty-interface */ -/* eslint-disable no-unused-vars */ -/** - * Interface for GL-ES3 Typization - * (yet to be implemented in ts-spec) - */ - -interface GLESObject {} - -interface GLESActiveInfo { - readonly name: string; - readonly size: number; - readonly type: number; -} - -interface GLESBuffer extends GLESObject {} - -interface GLESFramebuffer extends GLESObject {} - -interface GLESProgram extends GLESObject {} - -interface GLESRenderbuffer extends GLESObject {} - -interface GLESShader extends GLESObject {} - -interface GLESShaderPrecisionFormat { - readonly precision: number; - readonly rangeMax: number; - readonly rangeMin: number; -} - -interface GLESTexture extends GLESObject {} - -interface GLESUniformLocation {} - -declare interface GLESRenderingContext extends WebGL2RenderingContext { - activeTexture(texture: number): void; - attachShader(program: GLESProgram | null, shader: GLESShader | null): void; - bindAttribLocation( - program: GLESProgram | null, - index: number, - name: string - ): void; - bindBuffer(target: number, buffer: GLESBuffer | null): void; - bindFramebuffer(target: number, framebuffer: GLESFramebuffer | null): void; - bindRenderbuffer(target: number, renderbuffer: GLESRenderbuffer | null): void; - bindTexture(target: number, texture: GLESTexture | null): void; - blendColor(red: number, green: number, blue: number, alpha: number): void; - blendEquation(mode: number): void; - blendEquationSeparate(modeRGB: number, modeAlpha: number): void; - blendFunc(sfactor: number, dfactor: number): void; - blendFuncSeparate( - srcRGB: number, - dstRGB: number, - srcAlpha: number, - dstAlpha: number - ): void; - bufferData( - target: number, - size: number | ArrayBufferView | ArrayBuffer, - usage: number - ): void; - bufferSubData( - target: number, - offset: number, - data: ArrayBufferView | ArrayBuffer - ): void; - checkFramebufferStatus(target: number): number; - clear(mask: number): void; - clearColor(red: number, green: number, blue: number, alpha: number): void; - clearDepthf(depth: number): void; - clearStencil(s: number): void; - colorMask(red: boolean, green: boolean, blue: boolean, alpha: boolean): void; - compileShader(shader: GLESShader | null): void; - // compressedTexImage2D(target: number, level: number, internalformat: number, width: number, height: number, border: number, data: ArrayBufferView | ArrayBuffer): void; - // compressedTexSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, data: ArrayBufferView | ArrayBuffer): void; - copyTexImage2D( - target: number, - level: number, - internalformat: number, - x: number, - y: number, - width: number, - height: number, - border: number - ): void; - copyTexSubImage2D( - target: number, - level: number, - xoffset: number, - yoffset: number, - x: number, - y: number, - width: number, - height: number - ): void; - createBuffer(): GLESBuffer | null; - createFramebuffer(): GLESFramebuffer | null; - createProgram(): GLESProgram | null; - createRenderbuffer(): GLESRenderbuffer | null; - createShader(type: number): GLESShader | null; - createTexture(): GLESTexture | null; - cullFace(mode: number): void; - deleteBuffer(buffer: GLESBuffer | null): void; - deleteFramebuffer(framebuffer: GLESFramebuffer | null): void; - deleteProgram(program: GLESProgram | null): void; - deleteRenderbuffer(renderbuffer: GLESRenderbuffer | null): void; - deleteShader(shader: GLESShader | null): void; - deleteTexture(texture: GLESTexture | null): void; - depthFunc(func: number): void; - depthMask(flag: boolean): void; - depthRangef(zNear: number, zFar: number): void; - detachShader(program: GLESProgram | null, shader: GLESShader | null): void; - disable(cap: number): void; - disableVertexAttribArray(index: number): void; - drawArrays(mode: number, first: number, count: number): void; - drawElements(mode: number, count: number, type: number, offset: number): void; - enable(cap: number): void; - enableVertexAttribArray(index: number): void; - finish(): void; - flush(): void; - framebufferRenderbuffer( - target: number, - attachment: number, - renderbuffertarget: number, - renderbuffer: GLESRenderbuffer | null - ): void; - framebufferTexture2D( - target: number, - attachment: number, - textarget: number, - texture: GLESTexture | null, - level: number - ): void; - frontFace(mode: number): void; - generateMipmap(target: number): void; - getActiveAttrib( - program: GLESProgram | null, - index: number - ): GLESActiveInfo | null; - getActiveUniform( - program: GLESProgram | null, - index: number - ): GLESActiveInfo | null; - getAttachedShaders(program: GLESProgram | null): GLESShader[] | null; - getAttribLocation(program: GLESProgram | null, name: string): number; - getBooleanv(pname: number): boolean; - getBufferParameteriv(target: number, pname: number): number; - getError(): number; - getFloatv(pname: number): number; - getFramebufferAttachmentParameteriv( - target: number, - attachment: number, - pname: number - ): number; - getIntegerv(pname: number): number; - getProgramiv(program: GLESProgram, pname: number): number; - getProgramInfoLog(program: GLESProgram | null): string | null; - getRenderbufferParameteriv( - program: GLESProgram | null, - pname: number - ): number; - getShaderiv(shader: GLESShader, pname: number): number; - getShaderInfoLog(shader: GLESShader | null): string | null; - getShaderPrecisionFormat( - shadertype: number, - precisiontype: number - ): GLESShaderPrecisionFormat | null; - getShaderSource(shader: GLESShader | null): string | null; - getString(name: number): string; - getTexParameterfv(target: number, pname: number): number; - getTexParameteriv(target: number, pname: number): number; - getUniformfv( - program: GLESProgram | null, - location: GLESUniformLocation | null - ): number; - getUniformiv( - program: GLESProgram | null, - location: GLESUniformLocation | null - ): number; - getUniformLocation( - program: GLESProgram | null, - name: string - ): GLESUniformLocation | null; - getVertexAttribfv(index: number, pname: number): number; - getVertexAttribiv(index: number, pname: number): number; - getVertexAttribPointerv(index: number, pname: number): number; - hint(target: number, mode: number): void; - isBuffer(buffer: GLESBuffer | null): boolean; - isEnabled(cap: number): boolean; - isFramebuffer(framebuffer: GLESFramebuffer | null): boolean; - isProgram(program: GLESProgram | null): boolean; - isRenderbuffer(renderbuffer: GLESRenderbuffer | null): boolean; - isShader(shader: GLESShader | null): boolean; - isTexture(texture: GLESTexture | null): boolean; - lineWidth(width: number): void; - linkProgram(program: GLESProgram | null): void; - pixelStorei(pname: number, param: number | boolean): void; - polygonOffset(factor: number, units: number): void; - // readPixels(x: number, y: number, width: number, height: number, format: number, type: number, pixels: ArrayBufferView | null): void; - releaseShaderCompiler(): void; - renderbufferStorage( - target: number, - internalformat: number, - width: number, - height: number - ): void; - sampleCoverage(value: number, invert: boolean): void; - scissor(x: number, y: number, width: number, height: number): void; - shaderBinary( - shader: GLESShader, - binaryformat: number, - bin: ArrayBuffer | ArrayBufferView - ): void; - shaderSource(shader: GLESShader | null, source: string): void; - stencilFunc(func: number, ref: number, mask: number): void; - stencilFuncSeparate( - face: number, - func: number, - ref: number, - mask: number - ): void; - stencilMask(mask: number): void; - stencilMaskSeparate(face: number, mask: number): void; - stencilOp(fail: number, zfail: number, zpass: number): void; - stencilOpSeparate( - face: number, - fail: number, - zfail: number, - zpass: number - ): void; - // texImage2D(target: number, level: number, internalformat: number, width: number, height: number, border: number, format: number, type: number, pixels: ArrayBufferView | null): void; - texParameterf(target: number, pname: number, param: number): void; - texParameterfv(target: number, pname: number, params: Float32Array): void; - texParameteri(target: number, pname: number, param: number): void; - texParameteriv(target: number, pname: number, params: Int32Array): void; - // texSubImage2D(target: number, level: number, xoffset: number, yoffset: number, width: number, height: number, format: number, type: number, pixels: ArrayBufferView | null): void; - uniform1f(location: GLESUniformLocation | null, x: number): void; - uniform1fv(location: GLESUniformLocation, v: Float32Array): void; - uniform1i(location: GLESUniformLocation | null, x: number): void; - uniform1iv(location: GLESUniformLocation, v: Int32Array): void; - uniform2f(location: GLESUniformLocation | null, x: number, y: number): void; - uniform2fv(location: GLESUniformLocation, v: Float32Array): void; - uniform2i(location: GLESUniformLocation | null, x: number, y: number): void; - uniform2iv(location: GLESUniformLocation, v: Int32Array): void; - uniform3f( - location: GLESUniformLocation | null, - x: number, - y: number, - z: number - ): void; - uniform3fv(location: GLESUniformLocation, v: Float32Array): void; - uniform3i( - location: GLESUniformLocation | null, - x: number, - y: number, - z: number - ): void; - uniform3iv(location: GLESUniformLocation, v: Int32Array): void; - uniform4f( - location: GLESUniformLocation | null, - x: number, - y: number, - z: number, - w: number - ): void; - uniform4fv(location: GLESUniformLocation, v: Float32Array): void; - uniform4i( - location: GLESUniformLocation | null, - x: number, - y: number, - z: number, - w: number - ): void; - uniform4iv(location: GLESUniformLocation, v: Int32Array): void; - uniformMatrix2fv( - location: GLESUniformLocation, - transpose: boolean, - value: Float32Array - ): void; - uniformMatrix3fv( - location: GLESUniformLocation, - transpose: boolean, - value: Float32Array - ): void; - uniformMatrix4fv( - location: GLESUniformLocation, - transpose: boolean, - value: Float32Array - ): void; - useProgram(program: GLESProgram | null): void; - validateProgram(program: GLESProgram | null): void; - vertexAttrib1f(indx: number, x: number): void; - vertexAttrib1fv(indx: number, values: Float32Array): void; - vertexAttrib2f(indx: number, x: number, y: number): void; - vertexAttrib2fv(indx: number, values: Float32Array): void; - vertexAttrib3f(indx: number, x: number, y: number, z: number): void; - vertexAttrib3fv(indx: number, values: Float32Array): void; - vertexAttrib4f( - indx: number, - x: number, - y: number, - z: number, - w: number - ): void; - vertexAttrib4fv(indx: number, values: Float32Array): void; - vertexAttribPointer( - indx: number, - size: number, - type: number, - normalized: boolean, - stride: number, - offset: number - ): void; - viewport(x: number, y: number, width: number, height: number): void; - - readonly DEPTH_BUFFER_BIT: number; - readonly STENCIL_BUFFER_BIT: number; - readonly COLOR_BUFFER_BIT: number; - readonly FALSE: number; - readonly TRUE: number; - readonly POINTS: number; - readonly LINES: number; - readonly LINE_LOOP: number; - readonly LINE_STRIP: number; - readonly TRIANGLES: number; - readonly TRIANGLE_STRIP: number; - readonly TRIANGLE_FAN: number; - readonly ZERO: number; - readonly ONE: number; - readonly SRC_COLOR: number; - readonly ONE_MINUS_SRC_COLOR: number; - readonly SRC_ALPHA: number; - readonly ONE_MINUS_SRC_ALPHA: number; - readonly DST_ALPHA: number; - readonly ONE_MINUS_DST_ALPHA: number; - readonly DST_COLOR: number; - readonly ONE_MINUS_DST_COLOR: number; - readonly SRC_ALPHA_SATURATE: number; - readonly FUNC_ADD: number; - readonly BLEND_EQUATION: number; - readonly BLEND_EQUATION_RGB: number; - readonly BLEND_EQUATION_ALPHA: number; - readonly FUNC_SUBTRACT: number; - readonly FUNC_REVERSE_SUBTRACT: number; - readonly BLEND_DST_RGB: number; - readonly BLEND_SRC_RGB: number; - readonly BLEND_DST_ALPHA: number; - readonly BLEND_SRC_ALPHA: number; - readonly CONSTANT_COLOR: number; - readonly ONE_MINUS_CONSTANT_COLOR: number; - readonly CONSTANT_ALPHA: number; - readonly ONE_MINUS_CONSTANT_ALPHA: number; - readonly BLEND_COLOR: number; - readonly ARRAY_BUFFER: number; - readonly ELEMENT_ARRAY_BUFFER: number; - readonly ARRAY_BUFFER_BINDING: number; - readonly ELEMENT_ARRAY_BUFFER_BINDING: number; - readonly STREAM_DRAW: number; - readonly STATIC_DRAW: number; - readonly DYNAMIC_DRAW: number; - readonly BUFFER_SIZE: number; - readonly BUFFER_USAGE: number; - readonly CURRENT_VERTEX_ATTRIB: number; - readonly FRONT: number; - readonly BACK: number; - readonly FRONT_AND_BACK: number; - readonly TEXTURE_2D: number; - readonly CULL_FACE: number; - readonly BLEND: number; - readonly DITHER: number; - readonly STENCIL_TEST: number; - readonly DEPTH_TEST: number; - readonly SCISSOR_TEST: number; - readonly POLYGON_OFFSET_FILL: number; - readonly SAMPLE_ALPHA_TO_COVERAGE: number; - readonly SAMPLE_COVERAGE: number; - readonly NO_ERROR: number; - readonly INVALID_ENUM: number; - readonly INVALID_VALUE: number; - readonly INVALID_OPERATION: number; - readonly OUT_OF_MEMORY: number; - readonly CW: number; - readonly CCW: number; - readonly LINE_WIDTH: number; - readonly ALIASED_POINT_SIZE_RANGE: number; - readonly ALIASED_LINE_WIDTH_RANGE: number; - readonly CULL_FACE_MODE: number; - readonly FRONT_FACE: number; - readonly DEPTH_RANGE: number; - readonly DEPTH_WRITEMASK: number; - readonly DEPTH_CLEAR_VALUE: number; - readonly DEPTH_FUNC: number; - readonly STENCIL_CLEAR_VALUE: number; - readonly STENCIL_FUNC: number; - readonly STENCIL_FAIL: number; - readonly STENCIL_PASS_DEPTH_FAIL: number; - readonly STENCIL_PASS_DEPTH_PASS: number; - readonly STENCIL_REF: number; - readonly STENCIL_VALUE_MASK: number; - readonly STENCIL_WRITEMASK: number; - readonly STENCIL_BACK_FUNC: number; - readonly STENCIL_BACK_FAIL: number; - readonly STENCIL_BACK_PASS_DEPTH_FAIL: number; - readonly STENCIL_BACK_PASS_DEPTH_PASS: number; - readonly STENCIL_BACK_REF: number; - readonly STENCIL_BACK_VALUE_MASK: number; - readonly STENCIL_BACK_WRITEMASK: number; - readonly VIEWPORT: number; - readonly SCISSOR_BOX: number; - readonly COLOR_CLEAR_VALUE: number; - readonly COLOR_WRITEMASK: number; - readonly UNPACK_ALIGNMENT: number; - readonly PACK_ALIGNMENT: number; - readonly MAX_TEXTURE_SIZE: number; - readonly MAX_VIEWPORT_DIMS: number; - readonly SUBPIXEL_BITS: number; - readonly RED_BITS: number; - readonly GREEN_BITS: number; - readonly BLUE_BITS: number; - readonly ALPHA_BITS: number; - readonly DEPTH_BITS: number; - readonly STENCIL_BITS: number; - readonly POLYGON_OFFSET_UNITS: number; - readonly POLYGON_OFFSET_FACTOR: number; - readonly TEXTURE_BINDING_2D: number; - readonly SAMPLE_BUFFERS: number; - readonly SAMPLES: number; - readonly SAMPLE_COVERAGE_VALUE: number; - readonly SAMPLE_COVERAGE_INVERT: number; - readonly NUM_COMPRESSED_TEXTURE_FORMATS: number; - readonly COMPRESSED_TEXTURE_FORMATS: number; - readonly DONT_CARE: number; - readonly FASTEST: number; - readonly NICEST: number; - readonly GENERATE_MIPMAP_HINT: number; - readonly BYTE: number; - readonly UNSIGNED_BYTE: number; - readonly SHORT: number; - readonly UNSIGNED_SHORT: number; - readonly INT: number; - readonly UNSIGNED_INT: number; - readonly FLOAT: number; - readonly FIXED: number; - readonly DEPTH_COMPONENT: number; - readonly ALPHA: number; - readonly RGB: number; - readonly RGBA: number; - readonly LUMINANCE: number; - readonly LUMINANCE_ALPHA: number; - readonly UNSIGNED_SHORT_4_4_4_4: number; - readonly UNSIGNED_SHORT_5_5_5_1: number; - readonly UNSIGNED_SHORT_5_6_5: number; - readonly FRAGMENT_SHADER: number; - readonly VERTEX_SHADER: number; - readonly MAX_VERTEX_ATTRIBS: number; - readonly MAX_VERTEX_UNIFORM_VECTORS: number; - readonly MAX_VARYING_VECTORS: number; - readonly MAX_COMBINED_TEXTURE_IMAGE_UNITS: number; - readonly MAX_VERTEX_TEXTURE_IMAGE_UNITS: number; - readonly MAX_TEXTURE_IMAGE_UNITS: number; - readonly MAX_FRAGMENT_UNIFORM_VECTORS: number; - readonly SHADER_TYPE: number; - readonly DELETE_STATUS: number; - readonly LINK_STATUS: number; - readonly VALIDATE_STATUS: number; - readonly ATTACHED_SHADERS: number; - readonly ACTIVE_UNIFORMS: number; - readonly ACTIVE_UNIFORM_MAX_LENGTH: number; - readonly ACTIVE_ATTRIBUTES: number; - readonly ACTIVE_ATTRIBUTE_MAX_LENGTH: number; - readonly SHADING_LANGUAGE_VERSION: number; - readonly CURRENT_PROGRAM: number; - readonly NEVER: number; - readonly LESS: number; - readonly EQUAL: number; - readonly LEQUAL: number; - readonly GREATER: number; - readonly NOTEQUAL: number; - readonly GEQUAL: number; - readonly ALWAYS: number; - readonly KEEP: number; - readonly REPLACE: number; - readonly INCR: number; - readonly DECR: number; - readonly INVERT: number; - readonly INCR_WRAP: number; - readonly DECR_WRAP: number; - readonly VENDOR: number; - readonly RENDERER: number; - readonly VERSION: number; - readonly EXTENSIONS: number; - readonly NEAREST: number; - readonly LINEAR: number; - readonly NEAREST_MIPMAP_NEAREST: number; - readonly LINEAR_MIPMAP_NEAREST: number; - readonly NEAREST_MIPMAP_LINEAR: number; - readonly LINEAR_MIPMAP_LINEAR: number; - readonly TEXTURE_MAG_FILTER: number; - readonly TEXTURE_MIN_FILTER: number; - readonly TEXTURE_WRAP_S: number; - readonly TEXTURE_WRAP_T: number; - readonly TEXTURE: number; - readonly TEXTURE_CUBE_MAP: number; - readonly TEXTURE_BINDING_CUBE_MAP: number; - readonly TEXTURE_CUBE_MAP_POSITIVE_X: number; - readonly TEXTURE_CUBE_MAP_NEGATIVE_X: number; - readonly TEXTURE_CUBE_MAP_POSITIVE_Y: number; - readonly TEXTURE_CUBE_MAP_NEGATIVE_Y: number; - readonly TEXTURE_CUBE_MAP_POSITIVE_Z: number; - readonly TEXTURE_CUBE_MAP_NEGATIVE_Z: number; - readonly MAX_CUBE_MAP_TEXTURE_SIZE: number; - readonly TEXTURE0: number; - readonly TEXTURE1: number; - readonly TEXTURE2: number; - readonly TEXTURE3: number; - readonly TEXTURE4: number; - readonly TEXTURE5: number; - readonly TEXTURE6: number; - readonly TEXTURE7: number; - readonly TEXTURE8: number; - readonly TEXTURE9: number; - readonly TEXTURE10: number; - readonly TEXTURE11: number; - readonly TEXTURE12: number; - readonly TEXTURE13: number; - readonly TEXTURE14: number; - readonly TEXTURE15: number; - readonly TEXTURE16: number; - readonly TEXTURE17: number; - readonly TEXTURE18: number; - readonly TEXTURE19: number; - readonly TEXTURE20: number; - readonly TEXTURE21: number; - readonly TEXTURE22: number; - readonly TEXTURE23: number; - readonly TEXTURE24: number; - readonly TEXTURE25: number; - readonly TEXTURE26: number; - readonly TEXTURE27: number; - readonly TEXTURE28: number; - readonly TEXTURE29: number; - readonly TEXTURE30: number; - readonly TEXTURE31: number; - readonly ACTIVE_TEXTURE: number; - readonly REPEAT: number; - readonly CLAMP_TO_EDGE: number; - readonly MIRRORED_REPEAT: number; - readonly FLOAT_VEC2: number; - readonly FLOAT_VEC3: number; - readonly FLOAT_VEC4: number; - readonly INT_VEC2: number; - readonly INT_VEC3: number; - readonly INT_VEC4: number; - readonly BOOL: number; - readonly BOOL_VEC2: number; - readonly BOOL_VEC3: number; - readonly BOOL_VEC4: number; - readonly FLOAT_MAT2: number; - readonly FLOAT_MAT3: number; - readonly FLOAT_MAT4: number; - readonly SAMPLER_2D: number; - readonly SAMPLER_CUBE: number; - readonly VERTEX_ATTRIB_ARRAY_ENABLED: number; - readonly VERTEX_ATTRIB_ARRAY_SIZE: number; - readonly VERTEX_ATTRIB_ARRAY_STRIDE: number; - readonly VERTEX_ATTRIB_ARRAY_TYPE: number; - readonly VERTEX_ATTRIB_ARRAY_NORMALIZED: number; - readonly VERTEX_ATTRIB_ARRAY_POINTER: number; - readonly VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: number; - readonly IMPLEMENTATION_COLOR_READ_TYPE: number; - readonly IMPLEMENTATION_COLOR_READ_FORMAT: number; - readonly COMPILE_STATUS: number; - readonly INFO_LOG_LENGTH: number; - readonly SHADER_SOURCE_LENGTH: number; - readonly SHADER_COMPILER: number; - readonly SHADER_BINARY_FORMATS: number; - readonly NUM_SHADER_BINARY_FORMATS: number; - readonly LOW_FLOAT: number; - readonly MEDIUM_FLOAT: number; - readonly HIGH_FLOAT: number; - readonly LOW_INT: number; - readonly MEDIUM_INT: number; - readonly HIGH_INT: number; - readonly FRAMEBUFFER: number; - readonly RENDERBUFFER: number; - readonly RGBA4: number; - readonly RGB5_A1: number; - readonly RGB565: number; - readonly DEPTH_COMPONENT16: number; - readonly STENCIL_INDEX8: number; - readonly RENDERBUFFER_WIDTH: number; - readonly RENDERBUFFER_HEIGHT: number; - readonly RENDERBUFFER_INTERNAL_FORMAT: number; - readonly RENDERBUFFER_RED_SIZE: number; - readonly RENDERBUFFER_GREEN_SIZE: number; - readonly RENDERBUFFER_BLUE_SIZE: number; - readonly RENDERBUFFER_ALPHA_SIZE: number; - readonly RENDERBUFFER_DEPTH_SIZE: number; - readonly RENDERBUFFER_STENCIL_SIZE: number; - readonly FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: number; - readonly FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: number; - readonly FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: number; - readonly FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: number; - readonly COLOR_ATTACHMENT0: number; - readonly DEPTH_ATTACHMENT: number; - readonly STENCIL_ATTACHMENT: number; - readonly NONE: number; - readonly FRAMEBUFFER_COMPLETE: number; - readonly FRAMEBUFFER_INCOMPLETE_ATTACHMENT: number; - readonly FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: number; - readonly FRAMEBUFFER_INCOMPLETE_DIMENSIONS: number; - readonly FRAMEBUFFER_UNSUPPORTED: number; - readonly FRAMEBUFFER_BINDING: number; - readonly RENDERBUFFER_BINDING: number; - readonly MAX_RENDERBUFFER_SIZE: number; - readonly INVALID_FRAMEBUFFER_OPERATION: number; - readonly READ_BUFFER: number; - readonly UNPACK_ROW_LENGTH: number; - readonly UNPACK_SKIP_ROWS: number; - readonly UNPACK_SKIP_PIXELS: number; - readonly PACK_ROW_LENGTH: number; - readonly PACK_SKIP_ROWS: number; - readonly PACK_SKIP_PIXELS: number; - readonly COLOR: number; - readonly DEPTH: number; - readonly STENCIL: number; - readonly RED: number; - readonly RGB8: number; - readonly RGBA8: number; - readonly RGB10_A2: number; - readonly TEXTURE_BINDING_3D: number; - readonly UNPACK_SKIP_IMAGES: number; - readonly UNPACK_IMAGE_HEIGHT: number; - readonly TEXTURE_3D: number; - readonly TEXTURE_WRAP_R: number; - readonly MAX_3D_TEXTURE_SIZE: number; - readonly UNSIGNED_INT_2_10_10_10_REV: number; - readonly MAX_ELEMENTS_VERTICES: number; - readonly MAX_ELEMENTS_INDICES: number; - readonly TEXTURE_MIN_LOD: number; - readonly TEXTURE_MAX_LOD: number; - readonly TEXTURE_BASE_LEVEL: number; - readonly TEXTURE_MAX_LEVEL: number; - readonly MIN: number; - readonly MAX: number; - readonly DEPTH_COMPONENT24: number; - readonly MAX_TEXTURE_LOD_BIAS: number; - readonly TEXTURE_COMPARE_MODE: number; - readonly TEXTURE_COMPARE_FUNC: number; - readonly CURRENT_QUERY: number; - readonly QUERY_RESULT: number; - readonly QUERY_RESULT_AVAILABLE: number; - readonly BUFFER_MAPPED: number; - readonly BUFFER_MAP_POINTER: number; - readonly STREAM_READ: number; - readonly STREAM_COPY: number; - readonly STATIC_READ: number; - readonly STATIC_COPY: number; - readonly DYNAMIC_READ: number; - readonly DYNAMIC_COPY: number; - readonly MAX_DRAW_BUFFERS: number; - readonly DRAW_BUFFER0: number; - readonly DRAW_BUFFER1: number; - readonly DRAW_BUFFER2: number; - readonly DRAW_BUFFER3: number; - readonly DRAW_BUFFER4: number; - readonly DRAW_BUFFER5: number; - readonly DRAW_BUFFER6: number; - readonly DRAW_BUFFER7: number; - readonly DRAW_BUFFER8: number; - readonly DRAW_BUFFER9: number; - readonly DRAW_BUFFER10: number; - readonly DRAW_BUFFER11: number; - readonly DRAW_BUFFER12: number; - readonly DRAW_BUFFER13: number; - readonly DRAW_BUFFER14: number; - readonly DRAW_BUFFER15: number; - readonly MAX_FRAGMENT_UNIFORM_COMPONENTS: number; - readonly MAX_VERTEX_UNIFORM_COMPONENTS: number; - readonly SAMPLER_3D: number; - readonly SAMPLER_2D_SHADOW: number; - readonly FRAGMENT_SHADER_DERIVATIVE_HINT: number; - readonly PIXEL_PACK_BUFFER: number; - readonly PIXEL_UNPACK_BUFFER: number; - readonly PIXEL_PACK_BUFFER_BINDING: number; - readonly PIXEL_UNPACK_BUFFER_BINDING: number; - readonly FLOAT_MAT2x3: number; - readonly FLOAT_MAT2x4: number; - readonly FLOAT_MAT3x2: number; - readonly FLOAT_MAT3x4: number; - readonly FLOAT_MAT4x2: number; - readonly FLOAT_MAT4x3: number; - readonly SRGB: number; - readonly SRGB8: number; - readonly SRGB8_ALPHA8: number; - readonly COMPARE_REF_TO_TEXTURE: number; - readonly MAJOR_VERSION: number; - readonly MINOR_VERSION: number; - readonly NUM_EXTENSIONS: number; - readonly RGBA32F: number; - readonly RGB32F: number; - readonly RGBA16F: number; - readonly RGB16F: number; - readonly VERTEX_ATTRIB_ARRAY_INTEGER: number; - readonly MAX_ARRAY_TEXTURE_LAYERS: number; - readonly MIN_PROGRAM_TEXEL_OFFSET: number; - readonly MAX_PROGRAM_TEXEL_OFFSET: number; - readonly MAX_VARYING_COMPONENTS: number; - readonly TEXTURE_2D_ARRAY: number; - readonly TEXTURE_BINDING_2D_ARRAY: number; - readonly R11F_G11F_B10F: number; - readonly UNSIGNED_INT_10F_11F_11F_REV: number; - readonly RGB9_E5: number; - readonly UNSIGNED_INT_5_9_9_9_REV: number; - readonly TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: number; - readonly TRANSFORM_FEEDBACK_BUFFER_MODE: number; - readonly MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: number; - readonly TRANSFORM_FEEDBACK_VARYINGS: number; - readonly TRANSFORM_FEEDBACK_BUFFER_START: number; - readonly TRANSFORM_FEEDBACK_BUFFER_SIZE: number; - readonly TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: number; - readonly RASTERIZER_DISCARD: number; - readonly MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: number; - readonly MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: number; - readonly INTERLEAVED_ATTRIBS: number; - readonly SEPARATE_ATTRIBS: number; - readonly TRANSFORM_FEEDBACK_BUFFER: number; - readonly TRANSFORM_FEEDBACK_BUFFER_BINDING: number; - readonly RGBA32UI: number; - readonly RGB32UI: number; - readonly RGBA16UI: number; - readonly RGB16UI: number; - readonly RGBA8UI: number; - readonly RGB8UI: number; - readonly RGBA32I: number; - readonly RGB32I: number; - readonly RGBA16I: number; - readonly RGB16I: number; - readonly RGBA8I: number; - readonly RGB8I: number; - readonly RED_INTEGER: number; - readonly RGB_INTEGER: number; - readonly RGBA_INTEGER: number; - readonly SAMPLER_2D_ARRAY: number; - readonly SAMPLER_2D_ARRAY_SHADOW: number; - readonly SAMPLER_CUBE_SHADOW: number; - readonly UNSIGNED_INT_VEC2: number; - readonly UNSIGNED_INT_VEC3: number; - readonly UNSIGNED_INT_VEC4: number; - readonly INT_SAMPLER_2D: number; - readonly INT_SAMPLER_3D: number; - readonly INT_SAMPLER_CUBE: number; - readonly INT_SAMPLER_2D_ARRAY: number; - readonly UNSIGNED_INT_SAMPLER_2D: number; - readonly UNSIGNED_INT_SAMPLER_3D: number; - readonly UNSIGNED_INT_SAMPLER_CUBE: number; - readonly UNSIGNED_INT_SAMPLER_2D_ARRAY: number; - readonly BUFFER_ACCESS_FLAGS: number; - readonly BUFFER_MAP_LENGTH: number; - readonly BUFFER_MAP_OFFSET: number; - readonly DEPTH_COMPONENT32F: number; - readonly DEPTH32F_STENCIL8: number; - readonly FLOAT_32_UNSIGNED_INT_24_8_REV: number; - readonly FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: number; - readonly FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: number; - readonly FRAMEBUFFER_ATTACHMENT_RED_SIZE: number; - readonly FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: number; - readonly FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: number; - readonly FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: number; - readonly FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: number; - readonly FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: number; - readonly FRAMEBUFFER_DEFAULT: number; - readonly FRAMEBUFFER_UNDEFINED: number; - readonly DEPTH_STENCIL_ATTACHMENT: number; - readonly DEPTH_STENCIL: number; - readonly UNSIGNED_INT_24_8: number; - readonly DEPTH24_STENCIL8: number; - readonly UNSIGNED_NORMALIZED: number; - readonly DRAW_FRAMEBUFFER_BINDING: number; - readonly READ_FRAMEBUFFER: number; - readonly DRAW_FRAMEBUFFER: number; - readonly READ_FRAMEBUFFER_BINDING: number; - readonly RENDERBUFFER_SAMPLES: number; - readonly FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: number; - readonly MAX_COLOR_ATTACHMENTS: number; - readonly COLOR_ATTACHMENT1: number; - readonly COLOR_ATTACHMENT2: number; - readonly COLOR_ATTACHMENT3: number; - readonly COLOR_ATTACHMENT4: number; - readonly COLOR_ATTACHMENT5: number; - readonly COLOR_ATTACHMENT6: number; - readonly COLOR_ATTACHMENT7: number; - readonly COLOR_ATTACHMENT8: number; - readonly COLOR_ATTACHMENT9: number; - readonly COLOR_ATTACHMENT10: number; - readonly COLOR_ATTACHMENT11: number; - readonly COLOR_ATTACHMENT12: number; - readonly COLOR_ATTACHMENT13: number; - readonly COLOR_ATTACHMENT14: number; - readonly COLOR_ATTACHMENT15: number; - readonly COLOR_ATTACHMENT16: number; - readonly COLOR_ATTACHMENT17: number; - readonly COLOR_ATTACHMENT18: number; - readonly COLOR_ATTACHMENT19: number; - readonly COLOR_ATTACHMENT20: number; - readonly COLOR_ATTACHMENT21: number; - readonly COLOR_ATTACHMENT22: number; - readonly COLOR_ATTACHMENT23: number; - readonly COLOR_ATTACHMENT24: number; - readonly COLOR_ATTACHMENT25: number; - readonly COLOR_ATTACHMENT26: number; - readonly COLOR_ATTACHMENT27: number; - readonly COLOR_ATTACHMENT28: number; - readonly COLOR_ATTACHMENT29: number; - readonly COLOR_ATTACHMENT30: number; - readonly COLOR_ATTACHMENT31: number; - readonly FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: number; - readonly MAX_SAMPLES: number; - readonly HALF_FLOAT: number; - readonly MAP_READ_BIT: number; - readonly MAP_WRITE_BIT: number; - readonly MAP_INVALIDATE_RANGE_BIT: number; - readonly MAP_INVALIDATE_BUFFER_BIT: number; - readonly MAP_FLUSH_EXPLICIT_BIT: number; - readonly MAP_UNSYNCHRONIZED_BIT: number; - readonly RG: number; - readonly RG_INTEGER: number; - readonly R8: number; - readonly RG8: number; - readonly R16F: number; - readonly R32F: number; - readonly RG16F: number; - readonly RG32F: number; - readonly R8I: number; - readonly R8UI: number; - readonly R16I: number; - readonly R16UI: number; - readonly R32I: number; - readonly R32UI: number; - readonly RG8I: number; - readonly RG8UI: number; - readonly RG16I: number; - readonly RG16UI: number; - readonly RG32I: number; - readonly RG32UI: number; - readonly VERTEX_ARRAY_BINDING: number; - readonly R8_SNORM: number; - readonly RG8_SNORM: number; - readonly RGB8_SNORM: number; - readonly RGBA8_SNORM: number; - readonly SIGNED_NORMALIZED: number; - readonly PRIMITIVE_RESTART_FIXED_INDEX: number; - readonly COPY_READ_BUFFER: number; - readonly COPY_WRITE_BUFFER: number; - readonly COPY_READ_BUFFER_BINDING: number; - readonly COPY_WRITE_BUFFER_BINDING: number; - readonly UNIFORM_BUFFER: number; - readonly UNIFORM_BUFFER_BINDING: number; - readonly UNIFORM_BUFFER_START: number; - readonly UNIFORM_BUFFER_SIZE: number; - readonly MAX_VERTEX_UNIFORM_BLOCKS: number; - readonly MAX_FRAGMENT_UNIFORM_BLOCKS: number; - readonly MAX_COMBINED_UNIFORM_BLOCKS: number; - readonly MAX_UNIFORM_BUFFER_BINDINGS: number; - readonly MAX_UNIFORM_BLOCK_SIZE: number; - readonly MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: number; - readonly MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: number; - readonly UNIFORM_BUFFER_OFFSET_ALIGNMENT: number; - readonly ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: number; - readonly ACTIVE_UNIFORM_BLOCKS: number; - readonly UNIFORM_TYPE: number; - readonly UNIFORM_SIZE: number; - readonly UNIFORM_NAME_LENGTH: number; - readonly UNIFORM_BLOCK_INDEX: number; - readonly UNIFORM_OFFSET: number; - readonly UNIFORM_ARRAY_STRIDE: number; - readonly UNIFORM_MATRIX_STRIDE: number; - readonly UNIFORM_IS_ROW_MAJOR: number; - readonly UNIFORM_BLOCK_BINDING: number; - readonly UNIFORM_BLOCK_DATA_SIZE: number; - readonly UNIFORM_BLOCK_NAME_LENGTH: number; - readonly UNIFORM_BLOCK_ACTIVE_UNIFORMS: number; - readonly UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: number; - readonly UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: number; - readonly UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: number; - readonly INVALID_INDEX: number; - readonly MAX_VERTEX_OUTPUT_COMPONENTS: number; - readonly MAX_FRAGMENT_INPUT_COMPONENTS: number; - readonly MAX_SERVER_WAIT_TIMEOUT: number; - readonly OBJECT_TYPE: number; - readonly SYNC_CONDITION: number; - readonly SYNC_STATUS: number; - readonly SYNC_FLAGS: number; - readonly SYNC_FENCE: number; - readonly SYNC_GPU_COMMANDS_COMPLETE: number; - readonly UNSIGNALED: number; - readonly SIGNALED: number; - readonly ALREADY_SIGNALED: number; - readonly TIMEOUT_EXPIRED: number; - readonly CONDITION_SATISFIED: number; - readonly WAIT_FAILED: number; - readonly SYNC_FLUSH_COMMANDS_BIT: number; - readonly TIMEOUT_IGNORED: number; - readonly VERTEX_ATTRIB_ARRAY_DIVISOR: number; - readonly ANY_SAMPLES_PASSED: number; - readonly ANY_SAMPLES_PASSED_CONSERVATIVE: number; - readonly SAMPLER_BINDING: number; - readonly RGB10_A2UI: number; - readonly TEXTURE_SWIZZLE_R: number; - readonly TEXTURE_SWIZZLE_G: number; - readonly TEXTURE_SWIZZLE_B: number; - readonly TEXTURE_SWIZZLE_A: number; - readonly GREEN: number; - readonly BLUE: number; - readonly INT_2_10_10_10_REV: number; - readonly TRANSFORM_FEEDBACK: number; - readonly TRANSFORM_FEEDBACK_PAUSED: number; - readonly TRANSFORM_FEEDBACK_ACTIVE: number; - readonly TRANSFORM_FEEDBACK_BINDING: number; - readonly PROGRAM_BINARY_RETRIEVABLE_HINT: number; - readonly PROGRAM_BINARY_LENGTH: number; - readonly NUM_PROGRAM_BINARY_FORMATS: number; - readonly PROGRAM_BINARY_FORMATS: number; - readonly COMPRESSED_R11_EAC: number; - readonly COMPRESSED_SIGNED_R11_EAC: number; - readonly COMPRESSED_RG11_EAC: number; - readonly COMPRESSED_SIGNED_RG11_EAC: number; - readonly COMPRESSED_RGB8_ETC2: number; - readonly COMPRESSED_SRGB8_ETC2: number; - readonly COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: number; - readonly COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: number; - readonly COMPRESSED_RGBA8_ETC2_EAC: number; - readonly COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: number; - readonly TEXTURE_IMMUTABLE_FORMAT: number; - readonly MAX_ELEMENT_INDEX: number; - readonly NUM_SAMPLE_COUNTS: number; - readonly TEXTURE_IMMUTABLE_LEVELS: number; - readonly ES_VERSION_2_0: number; -} diff --git a/src/CComponent.ts b/src/CComponent.ts index 0f4a901..0771f39 100644 --- a/src/CComponent.ts +++ b/src/CComponent.ts @@ -25,7 +25,7 @@ export class CComponent { * main Settings, need to be overwritten with Specific settings * @public */ - public settings: CSettings = null; + public settings: CSettings = undefined; /** * will recursively try to set a setting with type and return success diff --git a/src/FPSta.ts b/src/FPSta.ts index 1832ad0..73f469b 100644 --- a/src/FPSta.ts +++ b/src/FPSta.ts @@ -57,7 +57,7 @@ export class FPStats extends CComponent { private gpuMS = 1; // audio - private auProvider: WEAS = null; + private auProvider: WEAS = undefined; private audHolder: HTMLElement; private audioMS = 1; private bpmHolder: HTMLDivElement; @@ -270,7 +270,7 @@ export class FPStats extends CComponent { } const getDetail = (type: string) => - result.breakdown.find(bd => !!bd && !!bd.types && !!bd.types.includes && bd.types.includes(type)) ?? null + result.breakdown.find(bd => !!bd && !!bd.types && !!bd.types.includes && bd.types.includes(type)) ?? undefined const addDetail = (gotDetail: any, text: string, gotElement: HTMLElement, insertAfter: HTMLElement) => { if (!!gotDetail && !isNaN(gotDetail.bytes)) { diff --git a/src/WEICUE.ts b/src/WEICUE.ts index c5fd6fa..327eb8f 100644 --- a/src/WEICUE.ts +++ b/src/WEICUE.ts @@ -7,9 +7,9 @@ * See LICENSE file in the project root for full license information. */ +import { ICUE } from "./@types/we-icue"; import { CComponent } from "./CComponent"; import { CSettings } from "./CSettings"; -import { ICUE } from "./ICUE"; import { Smallog } from "./Smallog"; import { getRealWindowSize, rgbToObj, waitReady } from "./Util"; import { WEAS } from "./weas/WEAS"; @@ -54,17 +54,17 @@ export class WEICUE extends CComponent { private cue: ICUE; private weas: WEAS; - private holder: HTMLDivElement = null; - private texter: HTMLDivElement = null; - private preview: HTMLDivElement = null; - private helperCanvas: HTMLCanvasElement = null; - private helperContext: CanvasRenderingContext2D = null; + private holder?: HTMLDivElement = undefined; + private texter?: HTMLDivElement = undefined; + private preview?: HTMLDivElement = undefined; + private helperCanvas?: HTMLCanvasElement = undefined; + private helperContext?: CanvasRenderingContext2D = undefined; private icueDevices = []; - private icueInterval = null; + private icueInterval = undefined; // preview time out - private prevTimeout = null; + private prevTimeout?: NodeJS.Timeout = undefined; // runtime values public settings: CUESettings = new CUESettings(); @@ -319,7 +319,7 @@ export class WEICUE extends CComponent { // reset timeout? if (this.prevTimeout) { clearTimeout(this.prevTimeout); - this.prevTimeout = null; + this.prevTimeout = undefined; } // update / show preview if (this.isAvailable && this.preview && this.settings.icue_mode == 1) { diff --git a/src/WEWA.ts b/src/WEWA.ts index d5df53d..d0434f8 100644 --- a/src/WEWA.ts +++ b/src/WEWA.ts @@ -16,11 +16,11 @@ const LogHead = "[WEWWA] "; const DefLang = "en-us"; /** - * WEWWA + * WEWA * - * Wallpaper Engine Web Wallpaper Adapter + * Wallpaper Engine Web Adapter * - * This is an aditional TS class to be included in your Typescript/Webpack Wallpaper Engine + * This is an aditional class to be included in your Typescript/Webpack Wallpaper Engine * Web-Wallpaper project - so you can test, run & configure it from a normal web browser. * * REQUIREMENTS: @@ -56,25 +56,26 @@ const DefLang = "en-us"; * * @public */ -export class WEWWA { - private audListener: string; +export class WEWA { + private audioListener: string; private propListener: string; private projectFile: string; private defLang: string; - private projectData: any = null; + private projectData?: any = undefined; // TODO type WE project.json - private htmlMenu: Element = null; - private htmlIcon: Element = null; + private htmlMenu?: Element = undefined; + private htmlIcon?: Element = undefined; - private audio: HTMLAudioElement = null; - private ctx: AudioContext = null; - private source: any = null; - private analyser: any = null; + private audio?: HTMLAudioElement = undefined; + private ctx?: AudioContext = undefined; - private audioInterval: any = null; - private audioCallback: any = null; + private source?: MediaStreamAudioSourceNode | MediaElementAudioSourceNode = undefined; + private analyser?: AnalyserNode = undefined; + + private audioInterval?: NodeJS.Timeout = undefined; + private audioCallback?: (data: number[]) => void = undefined; private pauseOnUnfocus = true; private isPaused = false; @@ -83,24 +84,24 @@ export class WEWWA { * Check if we are running in Web-Mode * if yes => iniitialize, else => do nothing * @param {Function} finished Callback for initializing the wallpaper - * @param {string} audListener to register + * @param {string} audioListener to register * @param {string} propListener to register * @param {string} projFile to register * @param {string} defLang default languague from project file */ constructor( finished: () => void = undefined, - audListener = "wallpaperRegisterAudioListener", + audioListener = "wallpaperRegisterAudioListener", propListener = "wallpaperPropertyListener", projFile = "project.json", defLang = DefLang ) { - this.audListener = audListener; + this.audioListener = audioListener; this.propListener = propListener; this.projectFile = projFile; this.defLang = defLang; - if (window[audListener]) { + if (window[audioListener]) { Smallog.info("detected wallpaper engine => Standby.", LogHead); if (finished !== undefined) finished(); return; @@ -109,7 +110,7 @@ export class WEWWA { Smallog.info("wallpaper engine not detected => Init!", LogHead); // define audio listener first, so we dont miss when it gets registered. - window[audListener] = (callback) => { + window[audioListener] = (callback) => { // set callback to be called later with analysed audio data this.audioCallback = callback; Smallog.info("Registered wallpaper AudioListener.", LogHead); @@ -318,7 +319,7 @@ export class WEWWA { if (this.htmlMenu) { document.body.removeChild(this.htmlMenu); document.body.removeChild(this.htmlIcon); - this.htmlMenu = null; + this.htmlMenu = undefined; } // quick wrapper, we need this a lot @@ -326,7 +327,7 @@ export class WEWWA { // local vars faster const proj = this.projectData; - const props = proj.general.properties; + const props = proj.general.properties; // TODO Type // create root menu this.htmlMenu = ce("div"); @@ -739,8 +740,10 @@ export class WEWWA { // Input const column2 = ce("td"); column2.classList.add("right"); - // optional NumericUpDown Column + + // optional NumericUpDown Column for sliders let column3 = null; + // div or label text element let txt = null; // main input element @@ -1196,7 +1199,7 @@ export class WEWWA { private startAudioInterval() { const data = new Uint8Array(128); // 33ms ~~ 30fps - this.audioInterval = window.setInterval(() => { + this.audioInterval = setInterval(() => { if (this.audioCallback == null) { this.stopAudioInterval(); Smallog.error("no AudioCallback!", LogHead); @@ -1219,7 +1222,7 @@ export class WEWWA { * @param {Uint8Array} data input * @returns {number[]} result */ - private convertAudio(data: Uint8Array) { + private convertAudio(data: Uint8Array): number[] { const stereo = []; let sIdx = 0; for (let i = 0; i < 64; i++) { diff --git a/src/three.ts b/src/three.ts index f5dc233..31cd576 160000 --- a/src/three.ts +++ b/src/three.ts @@ -1 +1 @@ -Subproject commit f5dc23341db58629f1e2e85219c60b0b2a610384 +Subproject commit 31cd5768f99d635bef903ca5fb7b7b8e0eb7990f diff --git a/src/three/EffectComposer.ts b/src/three/EffectComposer.ts index f767d5a..5a6350e 100644 --- a/src/three/EffectComposer.ts +++ b/src/three/EffectComposer.ts @@ -193,9 +193,9 @@ export class EffectComposer { if (this.renderer.autoClear) this.renderer.clear(); // do spilt rendering - if (this.renderer.xr.isPresenting && frame !== null) { + if (this.renderer.xr.isPresenting && frame !== undefined) { this.scene.updateMatrixWorld(); - if (this.camera.parent === null) this.camera.updateMatrixWorld(); + if (this.camera.parent === undefined) this.camera.updateMatrixWorld(); // update cameras const pose = frame.getViewerPose(this.renderer.xr.getReferenceSpace()); diff --git a/src/three/pass/FullScreenHelper.ts b/src/three/pass/FullScreenHelper.ts index 3aa910d..0b6f597 100644 --- a/src/three/pass/FullScreenHelper.ts +++ b/src/three/pass/FullScreenHelper.ts @@ -18,11 +18,11 @@ import { WebGLRenderer } from "three.ts/src/renderers/WebGLRenderer"; * @public */ export class FullScreenHelper { - private _mat = null; + private readonly _mat: Material; - public camera: Camera = null; - public geometry: BufferGeometry = null; - public mesh: Mesh = null; + public readonly camera: Camera; + public readonly geometry: BufferGeometry; + public readonly mesh: Mesh; /** * instantiate diff --git a/src/three/pass/RenderPass.ts b/src/three/pass/RenderPass.ts index 4786306..934ba70 100644 --- a/src/three/pass/RenderPass.ts +++ b/src/three/pass/RenderPass.ts @@ -16,39 +16,37 @@ import { BasePass } from "./BasePass"; * Shader Render Helper */ export class RenderPass implements BasePass { - name = "RenderPass"; - enabled = true; - needsSwap = true; + private readonly scene: Scene; + private readonly camera: Camera; + private readonly overMat?: Material; - clear = true; + public clearColor: Color = undefined; + public clearAlpha: number = undefined; - clearColor: Color = null; + public name = "RenderPass"; + public enabled = true; + public needsSwap = true; - clearAlpha: number = null; - clearDepth = false; - - scene: Scene = null; - camera: Camera = null; - overMat: Material = null; + public clear = true; + public clearDepth = false; /** * Construct helper * @param {Scene} scene Scene * @param {Camera} camera Camera - * @param {Material} overMat Override material + * @param {Material} overMat optional Override material * @param {Color} clearColor Clear color * @param {number} clearAlpha Clear alpha */ constructor( scene: Scene, camera: Camera, - overMat: Material, - clearColor?, + overMat?: Material, + clearColor?: Color, clearAlpha?: number ) { this.scene = scene; this.camera = camera; - this.overMat = overMat; this.clearColor = clearColor;

    )CdQ3F!eKyF|9D2V!F-rhUpJ8J+l7g0OBBVM#THTI&O8)>EGR%u6Gd9D9pm5!S}%&6DxW}^f|7=Y zPoO3(pTZY#?(7(|!5}5Nn!D%DotZmlW)?smSMcEE<^aT$6gw#LlwubPI9BYTffL0! zyu-EPCnz{Y#ZR&1d{F!hr_NW!&#~mXis$jseXDo@U)-kR7sMBeUt-T&RQw9By@BF9 z3f?cpmw4m-R{RHncaC**(V--ipJ<~6LkW2fi6RVfh%vcYt9@z>&M0LBSf-Q|Et8wU zCt43_*JB)mHR71wb`K@~5Cizwp{`A2uuJ^_Bcl3k{7ree$8&@l?;^2nagS+NqCDBfkB?pJws=PbK~+A7|2 z{gCDJKI-i%m4LD$n{WIwWR|c+NRy`C1#)1sSBI7FiH6z-QkhY&Q_|%I3exQ zQ`X1M?cZH4^M&BSyr;2z$+^SZUMA*0001Z+HKHROw(}?!13=vX`$@Br+fGR zZ%e`5O6%Txi$Yrz0gF{}p>fY>OnlS0Uevf}oDXW;D{d2gcE<2)oFcV80@g$H)63L{HN*d{8kVzKVW(;E)$9N_%kx5Ku3R9WJbY?JW^G#k0Wdx>E$NBBVtKRLiL?sA*s%w`TdsNz1=+~FRNdB8&+@iBD0 zXFTC4C-8-Cwv(4U=LLQ~^Oa4^rG|OTr5?ItoaPMYxxh`%a*kVU z;HYGAjq6;IY{`*awo0DlOMw(hkrYdb(O28l;MYvSx*ChcQW4f^QL5UdE3HbqvbxB$pfSg`>Cj#;?~00;nMAg}==M6d%RaIhCe zARtS)01i=0um)3FSgr#ump{<1pq_<0a34Kp8x=7I1^|9 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Regular-webfont.eot b/docs/fonts/OpenSans-Regular-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..6bbc3cf58cb011a6b4bf3cb1612ce212608f7274 GIT binary patch literal 19836 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Regular-webfont.woff b/docs/fonts/OpenSans-Regular-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e231183dce4c7b452afc9e7799586fd285e146f4 GIT binary patch literal 22660 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 ML;wL%uV=LY00kR;TmS$7 literal 0 HcmV?d00001 diff --git a/docs/global.html b/docs/global.html new file mode 100644 index 0000000..8c2768c --- /dev/null +++ b/docs/global.html @@ -0,0 +1,1102 @@ + + + + + + + + Global + + + + + + + + + + + + + + + + + + + + +