class VTUtils { static random(min, max) { let rand = Math.random(); if (typeof min === 'undefined') { return rand; } else if (typeof max === 'undefined') { if (min instanceof Array) { return min[Math.floor(rand * min.length)]; } else { return rand * min; } } else { if (min > max) { let tmp = min; min = max; max = tmp; } return rand * (max - min) + min; } }; static randomInt(min, max) { return Math.floor(VTUtils.random(min, max)); } static normalize(val, max, min) { return (val - min) / (max - min); }; static distance(x, y, x2, y2) { let a = x - x2; let b = y - y2; return Math.sqrt(a * a + b * b); } static map(n, start1, stop1, start2, stop2, withinBounds) { let newVal = (n - start1) / (stop1 - start1) * (stop2 - start2) + start2; if (!withinBounds) { return newVal; } if (start2 < stop2) { return this.constrain(newVal, start2, stop2); } else { return this.constrain(newVal, stop2, start2); } }; static constrain(n, low, high) { return Math.max(Math.min(n, high), low); } static hsvToRgb(h, s, v) { var r, g, b; var i = Math.floor(h * 6); var f = h * 6 - i; var p = v * (1 - s); var q = v * (1 - f * s); var t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; case 5: r = v, g = p, b = q; break; } return {r: r, g: g, b: b}; } static peakRGB(peak) { return { r: peak, g: 1 - peak, b: 0 }; } } class VTVector { constructor(x, y, z) { this.x = x || 0; this.y = y || 0; this.z = z || 0; } //helper static createRandom(x, y, z) { x = x || 1; y = y || 1; z = z || 0; return new VTVector(VTUtils.random(-x, x), VTUtils.random(-y, y), VTUtils.random(-z, z)); } mult(times) { this.x *= times; this.y *= times; this.z *= times; } set(vector) { this.x = vector.x; this.y = vector.y; this.z = vector.z; } add(vector) { this.x = this.x + vector.x; this.y = this.y + vector.y; this.z = this.z + vector.z; } addXYZ(x, y, z) { this.x += x; this.y += y; this.z += z; } setXYZ(x, y, z) { this.x = x || 0; this.y = y || 0; this.z = z || 0; } clone() { return new VTVector(this.x, this.y, this.z); } } class TDUtils { static multiply(a, b) { let b00 = b[0 * 4 + 0]; let b01 = b[0 * 4 + 1]; let b02 = b[0 * 4 + 2]; let b03 = b[0 * 4 + 3]; let b10 = b[1 * 4 + 0]; let b11 = b[1 * 4 + 1]; let b12 = b[1 * 4 + 2]; let b13 = b[1 * 4 + 3]; let b20 = b[2 * 4 + 0]; let b21 = b[2 * 4 + 1]; let b22 = b[2 * 4 + 2]; let b23 = b[2 * 4 + 3]; let b30 = b[3 * 4 + 0]; let b31 = b[3 * 4 + 1]; let b32 = b[3 * 4 + 2]; let b33 = b[3 * 4 + 3]; let a00 = a[0 * 4 + 0]; let a01 = a[0 * 4 + 1]; let a02 = a[0 * 4 + 2]; let a03 = a[0 * 4 + 3]; let a10 = a[1 * 4 + 0]; let a11 = a[1 * 4 + 1]; let a12 = a[1 * 4 + 2]; let a13 = a[1 * 4 + 3]; let a20 = a[2 * 4 + 0]; let a21 = a[2 * 4 + 1]; let a22 = a[2 * 4 + 2]; let a23 = a[2 * 4 + 3]; let a30 = a[3 * 4 + 0]; let a31 = a[3 * 4 + 1]; let a32 = a[3 * 4 + 2]; let a33 = a[3 * 4 + 3]; let dst = []; dst[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30; dst[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31; dst[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32; dst[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33; dst[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30; dst[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31; dst[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32; dst[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33; dst[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30; dst[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31; dst[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32; dst[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33; dst[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30; dst[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31; dst[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32; dst[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33; return dst; } static xRotation(angleInRadians) { let c = Math.cos(angleInRadians); let s = Math.sin(angleInRadians); return [ 1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, ]; } static yRotation(angleInRadians) { let c = Math.cos(angleInRadians); let s = Math.sin(angleInRadians); return [ c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1, ]; } static zRotation(angleInRadians) { let c = Math.cos(angleInRadians); let s = Math.sin(angleInRadians); return [ c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, ]; } static degToRad(d) { return d * Math.PI / 180; } } function $(sel, s) { return $$(sel, s)[0]; } function $$(sel, s) { s = s || document; return s.querySelectorAll(sel); } Node.prototype.addDelegatedEventListener = function (type, aim, cb) { this.addEventListener(type, (event) => { let target = event.target; if (target.matches(aim)) { cb(event, target); } else { let parent = target.closest(aim); if (parent) { cb(event, parent); } } }) }; Node.prototype.hasClass = function (className) { return this.classList.contains(className); } Node.prototype.addClass = function (className) { return this.classList.add(className); } Node.prototype.removeClass = function (className) { return this.classList.remove(className); } function create(name, content) { let d = document.createElement(name); if (content) { d.innerHTML = content; } return d; } function append(to, array) { for (let item in array) { to.appendChild(array[item]); } } class Template { constructor() { this.tpl = {}; } async loadTemplate(name) { let self = this; if (!this.tpl[name]) { await fetch(templateDir + name + ".tpl").then((r) => r.text()).then(c => { self.tpl[name] = c; }) } } async loadArray(names) { for (let name of names) { await this.loadTemplate(name); } } parseTemplate(name, data) { if (!this.tpl[name]) { return "" } let m, d = this.tpl[name]; while ((m = templateEx.exec(d)) !== null) { if (m.index === templateEx.lastIndex) { templateEx.lastIndex++; } let key = m[0]; d = d.replace(key, data[m[1]] || "") } return d; } parseFromAPI(url, name, cb) { fetch(url).then((r) => r.json()).then(d => { cb(this.parseTemplate(name, d)) }).catch(console.error) } } const templateEx = /\$(.*?)\$/gm; const templateDir = "out/tpl/" class ShaderHandler { constructor(gl) { this.gl = gl; this.shaderNames = []; this.shaders = {}; this.programs = {}; } setGL(gl) { this.gl = gl; } async loadShader(name, path) { this.shaderNames.push(name); await this.load(name, path + name + ".vert", this.gl.VERTEX_SHADER); await this.load(name, path + name + ".frag", this.gl.FRAGMENT_SHADER); } async load(name, url, type) { let realName = name + "_" + type; if (!this.shaders[realName]) { let data = await fetch(url); let shader = this.createShader(await data.text(), type); if (shader) { this.shaders[realName] = shader; } } return !!this.shaders[realName]; } getShader(name, type) { let realName = name + "_" + type; return this.shaders[realName]; } getAllShaders() { return this.shaderNames; } async createProgramForEach(arr) { arr = arr || this.shaderNames; for (let i = 0; i < arr.length; i++) { let shader = arr[i]; let v = await shaderHandler.createProgram(shader, [shader]) if (!v) { return false; } } return true; } createShader(source, type) { let gl = this.gl; let shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { return shader; } console.error(gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } createProgram(name, shaders) { let gl = this.gl; let pro = gl.createProgram(); for (let i = 0; i < shaders.length; i++) { gl.attachShader(pro, this.getShader(shaders[i], gl.VERTEX_SHADER)); gl.attachShader(pro, this.getShader(shaders[i], gl.FRAGMENT_SHADER)); } gl.linkProgram(pro); var success = gl.getProgramParameter(pro, gl.LINK_STATUS); if (success) { this.programs[name] = pro; return pro; } console.log(gl.getProgramInfoLog(pro)); gl.deleteProgram(pro); return null; } getProgram(name) { return this.programs[name]; } use(name) { let pro = this.programs[name]; this.gl.useProgram(pro); return pro; } async loadArray(list, path) { let self = this; for (const e of list) { await self.loadShader(e, path) } await self.createProgramForEach(list) } } const AudioContext = window.AudioContext || window.webkitAudioContext; class AudioHandler { async init() { let self = this; self.isStarted = false; self.audioFile = new Audio(); self.actx = new AudioContext() self.analyser = self.actx.createAnalyser() self.analyser.fftSize = 4096; self.lastSong = null; await self.connectAll(); } async connectAll() { let self = this; self.source = self.actx.createMediaElementSource(self.audioFile); self.source.connect(self.analyser); self.analyser.connect(self.actx.destination); self.audioFile.addEventListener('ended', player.nextSong.bind(player)); } async start() { if (this.audioFile.src === '') { return; } if (!this.isStarted) { this.isStarted = true; await this.actx.resume(); } } async stop() { if (this.isStarted) { this.isStarted = false; await this.actx.suspend(); } } fftSize(size) { this.analyser.fftSize = size; } smoothing(float) { this.analyser.smoothingTimeConstant = float; } loadSong(src) { let self = this; if (self.lastSong) { URL.revokeObjectURL(self.lastSong); } self.lastSong = this.audioFile.src = URL.createObjectURL(src); if (!this.isStarted) { this.start(); } this.audioFile.play(); } getIntArray(steps) { let dataArray = new Uint8Array(steps); this.analyser.getByteFrequencyData(dataArray); return dataArray; } getFloatArray() { let dataArray = new Float32Array(this.analyser.frequencyBinCount); this.analyser.getFloatTimeDomainData(dataArray); return dataArray; } } class Player { async init() { this.playlist = new Playlist(); } nextSong() { let next = this.playlist.getNext(); audioHandler.loadSong(next.file); } prevSong() { let next = this.playlist.getPrevious(); audioHandler.loadSong(next.file); } playStop() { if (!audioHandler.lastSong) { let next = this.playlist.getCurrent(); audioHandler.loadSong(next.file); } let audioFile = audioHandler.audioFile; if (audioFile.paused) { audioFile.play(); } else { audioFile.pause(); } } playByID(number) { let next = this.playlist.getFile(number); audioHandler.loadSong(next.file); } } const PAGINATIONLIMIT = 50; class Playlist { constructor() { this.list = []; this.shuffled = []; this.index = 0; this.page = 0; this.isShuffle = false; $('body').addDelegatedEventListener('change', 'input[type="file"]', this.changeFiles.bind(this)); $('body').addDelegatedEventListener('click', '.pagination .item', this.handlePagination.bind(this)); } shuffle() { // only shuffle if more then 2 elements are in let len = this.list.length; if (len < 3) { this.shuffled = this.list; } // the current-list need to be shuffled... for (let i = 0; i < len; i++) { let random = VTUtils.randomInt(0, len - 1); this.swap(i, random); } } swap(a, b) { this.shuffled[a] = this.list[b]; this.shuffled[b] = this.list[a]; } getNext() { let items = this.isShuffle ? this.shuffled : this.list, len = items.length - 1, next = this.index + 1; if (next > len) { next = 0; } this.index = next; return items[next]; } getPrevious() { let items = this.isShuffle ? this.shuffled : this.list, len = items.length - 1, next = this.index - 1; if (next < 0) { next = len; } this.index = next; return items[next]; } getCurrent() { let items = this.isShuffle ? this.shuffled : this.list; return items[this.index]; } getFile(index) { let items = this.isShuffle ? this.shuffled : this.list; return items[index]; } // on new upload... this has to be an array! setPlaylist(files) { this.index = 0; this.list = files; this.shuffle(); } handlePagination(event, el) { if (el.hasClass('inactive')) { return; } if (el.hasClass('next-site')) { this.renderPagination(this.page + 1); } else { this.renderPagination(this.page - 1); } } renderPagination(page) { let length = this.list.length, maxSite = Math.ceil(length / PAGINATIONLIMIT) - 1; if (page < 0) { page = 0; } if (page > maxSite) { page = maxSite; } let s = page * PAGINATIONLIMIT, e = s + PAGINATIONLIMIT, data = ""; this.page = page; if (e >= length) { e = length; } if (length > 0) { let items = this.isShuffle ? this.shuffled : this.list; for (let i = s; i < e; i++) { let obj = { index: i, nr: i + 1, title: items[i].name } data += template.parseTemplate("playlist-item", obj); } } else { data = "