This commit is contained in:
Maurice Grönwoldt 2020-08-07 19:31:30 +02:00
parent 25fcefcb50
commit 07b35b9667
27 changed files with 429 additions and 79 deletions

View file

@ -30,6 +30,7 @@ const config = {
...visuals, ...visuals,
basePath + 'eventHandler.js', basePath + 'eventHandler.js',
basePath + 'select.js', basePath + 'select.js',
basePath + 'keys.js',
basePath + 'startup.js', basePath + 'startup.js',
basePath + 'app.js' basePath + 'app.js'
], ],

View file

@ -43,6 +43,7 @@ function buildIconSprites() {
fal.faFolderUpload, fal.faFolderUpload,
fal.faListMusic, fal.faListMusic,
fal.faFileImage, fal.faFileImage,
fal.faQuestionCircle,
], ],
vt: [] vt: []
}; };

View file

@ -36,6 +36,11 @@
<use href="out/icon-sprite.svg#fal-fa-list-music"></use> <use href="out/icon-sprite.svg#fal-fa-list-music"></use>
</svg> </svg>
</div> </div>
<div class="help menu-icon">
<svg role="img" class="icon">
<use href="out/icon-sprite.svg#fal-fa-question-circle"></use>
</svg>
</div>
</div> </div>
<div class="controls"> <div class="controls">
<button id="previous"> <button id="previous">

View file

@ -1 +1 @@
[{"group":"","name":"fftSize","showName":"FFT-Size","options":[2048,4096,8192,16384],"value":4096,"type":"select","tooltip":"How Many Items should the FFT Capture and Render","dataType":"int"},{"group":"rotation","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":360,"min":-360,"value":0,"dataType":"int"},{"group":"rotation","name":["X-Inc","Y-Inc","Z-Inc"],"props":["x-inc","y-inc","z-inc"],"type":"slider","max":1,"min":-1,"value":0,"stepSize":0.01,"dataType":"float"},{"group":"","name":"baseColor","showName":"Base Color","type":"color","value":"#0089ff","dataType":"rgb","tooltip":"Base Color!"},{"group":"","name":"gradientToColor","showName":"Second Color","type":"color","value":"#ff0000","dataType":"rgb","tooltip":"Second Color!"}] [{"group":"","name":"fftSize","showName":"FFT-Size","options":[2048,4096,8192,16384],"value":4096,"type":"select","tooltip":"How Many Items should the FFT Capture and Render","dataType":"int"},{"group":"rotation","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":360,"min":-360,"value":0,"dataType":"int"},{"group":"rotation","name":["X-Inc","Y-Inc","Z-Inc"],"props":["x-inc","y-inc","z-inc"],"type":"slider","max":1,"min":-1,"value":0,"stepSize":0.01,"dataType":"float"},{"group":"light","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":100,"min":-100,"value":0,"dataType":"int"},{"group":"light","name":"light-strength","showName":"Light Brightness","type":"slider","value":0.3,"max":1,"min":0,"stepSize":0.05,"tooltip":"brightness of light-point","dataType":"float"},{"group":"","name":"baseColor","showName":"Base Color","type":"color","value":"#0089ff","dataType":"rgb","tooltip":"Base Color!"},{"group":"","name":"gradientToColor","showName":"Second Color","type":"color","value":"#ff0000","dataType":"rgb","tooltip":"Second Color!"}]

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View file

@ -224,6 +224,10 @@ Node.prototype.toggleCheck = function (className, force) {
} }
} }
String.prototype.firstUpper = function () {
return this.charAt(0).toUpperCase() + this.slice(1);
}
File.prototype.toBase64 = function (cb) { File.prototype.toBase64 = function (cb) {
const reader = new FileReader(); const reader = new FileReader();
reader.onloadend = cb; reader.onloadend = cb;
@ -1031,7 +1035,7 @@ class Playlist {
this.shuffled = []; this.shuffled = [];
this.index = 0; this.index = 0;
this.page = 0; this.page = 0;
this.isShuffle = false; this.isShuffle = pConf.get("shuffle", false);
$('body').addDelegatedEventListener('change', 'input[type="file"]', this.changeFiles.bind(this)); $('body').addDelegatedEventListener('change', 'input[type="file"]', this.changeFiles.bind(this));
$('body').addDelegatedEventListener('click', '.pagination .item', this.handlePagination.bind(this)); $('body').addDelegatedEventListener('click', '.pagination .item', this.handlePagination.bind(this));
eventHandler.addEvent('id3-request', this.handle.bind(this)); eventHandler.addEvent('id3-request', this.handle.bind(this));
@ -1228,11 +1232,17 @@ class GUI {
'inputs/slider', 'inputs/slider',
'inputs/switch', 'inputs/switch',
'inputs/select', 'inputs/select',
'inputs/option' 'inputs/option',
'help',
]); ]);
this.initDropZone(); this.initDropZone();
} }
openHelp() {
gui.modal.renderModal("Help", template.parseTemplate('help', {}));
gui.modal.showModal();
}
initDropZone() { initDropZone() {
let items = 'drag dragstart dragend dragover dragenter dragleave drop'.split(' '); let items = 'drag dragstart dragend dragover dragenter dragleave drop'.split(' ');
items.forEach(el => { items.forEach(el => {
@ -1253,6 +1263,7 @@ class GUI {
} }
// create config Inputs from JSON // create config Inputs from JSON
//@todo add support for gui grouping!
class GUIHelper { class GUIHelper {
static fromJSON(json, conf) { static fromJSON(json, conf) {
let data = []; let data = [];
@ -1289,7 +1300,7 @@ class GUIHelper {
for (let i = 0; i < data.name.length; i++) { for (let i = 0; i < data.name.length; i++) {
let newData = {}; let newData = {};
Object.assign(newData, data); Object.assign(newData, data);
newData.showName = data.name[i]; newData.showName = GUIHelper.richShowName(data, data.name[i].firstUpper());
newData.name = GUIHelper.richName(data, data.props[i]); newData.name = GUIHelper.richName(data, data.props[i]);
content += GUIHelper.createSlider(newData, conf); content += GUIHelper.createSlider(newData, conf);
} }
@ -1350,6 +1361,13 @@ class GUIHelper {
return name; return name;
} }
static richShowName(data, name) {
if (data.group !== "") {
return data.group.firstUpper() + ' ' + name
}
return name;
}
static createButton(item, conf) { static createButton(item, conf) {
return `<div class='button spaced' data-action="${item.action}">${item.name}</div>` return `<div class='button spaced' data-action="${item.action}">${item.name}</div>`
} }
@ -1632,7 +1650,7 @@ class Notification {
self.type += ' endless'; self.type += ' endless';
} }
self.updateContent(self.message); self.updateContent(self.message);
this.outer.appendChild(self.item); this.outer.prepend(self.item);
if (!endless) { if (!endless) {
setTimeout(this.remove.bind(this), self.time) setTimeout(this.remove.bind(this), self.time)
} }
@ -1800,8 +1818,14 @@ class Wave extends Visual {
prepare(program) { prepare(program) {
this.position = gl.getAttribLocation(program, "a_position"); this.position = gl.getAttribLocation(program, "a_position");
this.color = gl.getUniformLocation(program, "u_color"); this.color = gl.getUniformLocation(program, "u_color");
let lightPos = gl.getUniformLocation(program, "u_lightPos"); let lightPos = gl.getUniformLocation(program, "u_lightPos"),
gl.uniform3fv(lightPos, vConf.get("light", [0, 5, -56])); light = gl.getUniformLocation(program, "u_light");
gl.uniform3fv(lightPos, [
vConf.get("light-x", 0),
vConf.get("light-y", 5),
vConf.get("light-z", -56)
]);
gl.uniform1f(light, parseFloat(vConf.get("light-strength", 0.3)));
} }
afterDraw() { afterDraw() {
@ -1904,8 +1928,11 @@ class EventHandler {
this.events = {}; this.events = {};
} }
addEvent(name, cb) { addEvent(events, cb) {
this.events[name] = cb; let names = events.split(",");
for (let name of names) {
this.events[name.trim()] = cb;
}
} }
sendData(name, data) { sendData(name, data) {
@ -1918,12 +1945,18 @@ class EventHandler {
handleEvent(event) { handleEvent(event) {
let data = event.data; let data = event.data;
if (!data.cmd) { if (!data.cmd) {
return; return false;
} }
if (this.events[data.cmd]) { if (this.events[data.cmd]) {
try {
this.events[data.cmd](data.data); this.events[data.cmd](data.data);
} catch (e) {
console.error('[EventHandler] > ' + e.message);
} }
return true;
}
return false;
} }
} }
@ -1952,20 +1985,16 @@ async function initHandler() {
player.playStop(); player.playStop();
break; break;
case 'shuffle': case 'shuffle':
player.playlist.isShuffle = !player.playlist.isShuffle;
toggleShuffle(); toggleShuffle();
break; break;
} }
togglePlayButton(audioHandler.audioFile.paused ? 'play' : 'pause');
}); });
window.addEventListener('playSong', setActiveOnPlaylist); window.addEventListener('playSong', setActiveOnPlaylist);
window.addEventListener('playSong', e => {
togglePlayButton(audioHandler.audioFile.paused ? 'play' : 'pause');
});
$('.upload-image').addEventListener('click', imageUploader.renderModal.bind(imageUploader)); $('.upload-image').addEventListener('click', imageUploader.renderModal.bind(imageUploader));
body.addDelegatedEventListener('click', '.readAll', e => { body.addDelegatedEventListener('click', '.readAll', forceAllRead);
let playlist = player.playlist.list;
for (let i = 0; i < playlist.length; i++) {
playlist[i].getID3Tag(true);
}
})
body.addDelegatedEventListener('input', '.input-range input[type="range"]', (e, el) => { body.addDelegatedEventListener('input', '.input-range input[type="range"]', (e, el) => {
let current = $('.current', el.parentNode); let current = $('.current', el.parentNode);
@ -2013,6 +2042,23 @@ async function initHandler() {
break; break;
} }
}) })
$('.help.menu-icon').addEventListener('click', gui.openHelp);
document.onfullscreenchange = e => {
if (body.hasClass('fullscreen')) {
body.removeClass('fullscreen')
} else {
body.addClass('fullscreen')
}
}
}
function forceAllRead() {
let playlist = player.playlist.list;
for (let i = 0; i < playlist.length; i++) {
playlist[i].getID3Tag(true);
}
} }
@ -2039,11 +2085,17 @@ function setActiveOnPlaylist(e) {
} }
} }
function toggleShuffle() { function toggleShuffle(updateGUI) {
let active = player.playlist.isShuffle; let active = player.playlist.isShuffle;
$('#shuffle').toggleCheck('active', active); if (updateGUI !== false) {
active = !active;
let status = active ? 'enabled' : 'disabled'; let status = active ? 'enabled' : 'disabled';
NotificationHandler.createNotification("Shuffle: " + status, "info", 500); NotificationHandler.createNotification("Shuffle: " + status, "info", 500);
pConf.set("shuffle", active);
pConf.save();
player.playlist.isShuffle = active;
}
$('#shuffle').toggleCheck('active', active);
} }
function togglePlayButton(status) { function togglePlayButton(status) {
@ -2121,6 +2173,65 @@ function togglePlayButton(status) {
} }
})() })()
class KeyHandler {
async init() {
await this.mediaKeys();
await this.addKeyHandler();
window.addEventListener('keydown', this.keyHandler.bind(this));
}
async mediaKeys() {
if ('mediaSession' in navigator) {
let media = navigator.mediaSession;
media.setActionHandler('play', player.playStop.bind(player));
media.setActionHandler('pause', player.playStop.bind(player));
media.setActionHandler('previoustrack', player.prevSong.bind(player));
media.setActionHandler('nexttrack', player.prevSong.bind(player));
}
}
async addKeyHandler() {
eventHandler.addEvent('keys-Space', player.playStop.bind(player));
eventHandler.addEvent('keys-KeyN', player.nextSong.bind(player));
eventHandler.addEvent('keys-KeyV', player.prevSong.bind(player));
eventHandler.addEvent('keys-KeyS', playerConf.open.bind(playerConf));
eventHandler.addEvent('keys-KeyS-shift', toggleShuffle);
eventHandler.addEvent('keys-KeyB', imageUploader.renderModal.bind(imageUploader));
eventHandler.addEvent('keys-KeyF-shift', forceAllRead);
eventHandler.addEvent('keys-KeyH', gui.openHelp);
eventHandler.addEvent('keys-KeyP', e => {
player.playlist.renderPagination(player.playlist.page);
gui.modal.showModal();
});
eventHandler.addEvent('keys-Escape, keys-KeyC-shift', e => {
gui.modal.resetModal();
gui.modal.closeModal();
})
eventHandler.addEvent('keys-F11', e => {
if (document.fullscreenElement) {
document.exitFullscreen().catch(console.error);
} else {
document.body.requestFullscreen().catch(console.error);
}
});
}
async keyHandler(event) {
let key = event.code,
shift = event.shiftKey ? '-shift' : '',
ctrl = event.ctrlKey ? '-ctrl' : '',
name = 'keys-' + key + shift + ctrl;
if (eventHandler.handleEvent({
data: {
cmd: name
}
})) {
event.preventDefault();
event.stopPropagation();
}
}
}
class Startup { class Startup {
constructor() { constructor() {
this.modules = { this.modules = {
@ -2155,7 +2266,8 @@ const shaderHandler = new ShaderHandler(null),
worker = new Worker('/out/js/worker.min.js'), worker = new Worker('/out/js/worker.min.js'),
startup = new Startup(), startup = new Startup(),
eventHandler = new EventHandler(), eventHandler = new EventHandler(),
playerConf = new PlayerConfigHandler(); playerConf = new PlayerConfigHandler(),
keyHandler = new KeyHandler();
let c, gl, cInfo, ctx; let c, gl, cInfo, ctx;
@ -2192,7 +2304,9 @@ async function startUP() {
await gui.init(); await gui.init();
await imageUploader.init(); await imageUploader.init();
await playerConf.init(); await playerConf.init();
await keyHandler.init();
await initHandler(); await initHandler();
toggleShuffle(false);
} }
startUP().then(r => { startUP().then(r => {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

69
out/tpl/help.tpl Normal file
View file

@ -0,0 +1,69 @@
<!-- help for every key! -->
<div class="help-list">
<div class="item">
<span class="h2">h</span>
<p>
Open Help Modal
</p>
</div>
<div class="item">
<span class="h2">Space</span>
<p>
Switch between pause and play
</p>
</div>
<div class="item">
<span class="h2">n</span>
<p>
Play next song
</p>
</div>
<div class="item">
<span class="h2">v</span>
<p>
Play previous song
</p>
</div>
<div class="item">
<span class="h2">s</span>
<p>
Open Settings Modal
</p>
</div>
<div class="item">
<span class="h2">Shift + s</span>
<p>
Toggle Shuffle
</p>
</div>
<div class="item">
<span class="h2">b</span>
<p>
Open background settings Modal
</p>
</div>
<div class="item">
<span class="h2">Shift + f</span>
<p>
Force Song Tagger!
</p>
</div>
<div class="item">
<span class="h2">p</span>
<p>
Show Playlist Modal
</p>
</div>
<div class="item">
<span class="h2">ESC or Shift + C</span>
<p>
Close current open Modal
</p>
</div>
<div class="item">
<span class="h2">F11</span>
<p>
Toggle Fullscreen and hide GUI
</p>
</div>
</div>

View file

@ -19,10 +19,11 @@
<span class="focus"></span> <span class="focus"></span>
</label> </label>
<label class="input floating-label"> <label class="input floating-label input-range">
<input class="range" type="range" id="alphaValue" min="0" max="1" value="$alphaValue$" step="0.1"> <input class="range" type="range" id="alphaValue" min="0" max="1" value="$alphaValue$" step="0.1">
<span class="input-label">Alpha</span> <span class="input-label">Alpha</span>
<span class="min">0</span> <span class="min">0</span>
<span class="current">$alphaValue$</span>
<span class="max">1</span> <span class="max">1</span>
</label> </label>
</form> </form>

View file

@ -51,6 +51,36 @@
"stepSize": 0.01, "stepSize": 0.01,
"dataType": "float" "dataType": "float"
}, },
{
"group": "light",
"name": [
"X",
"Y",
"Z"
],
"props": [
"x",
"y",
"z"
],
"type": "slider",
"max": 100,
"min": -100,
"value": 0,
"dataType": "int"
},
{
"group": "light",
"name": "light-strength",
"showName": "Light Brightness",
"type": "slider",
"value": 0.3,
"max": 1,
"min": 0,
"stepSize": 0.05,
"tooltip": "brightness of light-point",
"dataType": "float"
},
{ {
"group": "", "group": "",
"name": "baseColor", "name": "baseColor",

View file

@ -9,7 +9,8 @@ const shaderHandler = new ShaderHandler(null),
worker = new Worker('/out/js/worker.min.js'), worker = new Worker('/out/js/worker.min.js'),
startup = new Startup(), startup = new Startup(),
eventHandler = new EventHandler(), eventHandler = new EventHandler(),
playerConf = new PlayerConfigHandler(); playerConf = new PlayerConfigHandler(),
keyHandler = new KeyHandler();
let c, gl, cInfo, ctx; let c, gl, cInfo, ctx;
@ -46,7 +47,9 @@ async function startUP() {
await gui.init(); await gui.init();
await imageUploader.init(); await imageUploader.init();
await playerConf.init(); await playerConf.init();
await keyHandler.init();
await initHandler(); await initHandler();
toggleShuffle(false);
} }
startUP().then(r => { startUP().then(r => {

View file

@ -3,8 +3,11 @@ class EventHandler {
this.events = {}; this.events = {};
} }
addEvent(name, cb) { addEvent(events, cb) {
this.events[name] = cb; let names = events.split(",");
for (let name of names) {
this.events[name.trim()] = cb;
}
} }
sendData(name, data) { sendData(name, data) {
@ -17,12 +20,18 @@ class EventHandler {
handleEvent(event) { handleEvent(event) {
let data = event.data; let data = event.data;
if (!data.cmd) { if (!data.cmd) {
return; return false;
} }
if (this.events[data.cmd]) { if (this.events[data.cmd]) {
try {
this.events[data.cmd](data.data); this.events[data.cmd](data.data);
} catch (e) {
console.error('[EventHandler] > ' + e.message);
} }
return true;
}
return false;
} }
} }
@ -51,20 +60,16 @@ async function initHandler() {
player.playStop(); player.playStop();
break; break;
case 'shuffle': case 'shuffle':
player.playlist.isShuffle = !player.playlist.isShuffle;
toggleShuffle(); toggleShuffle();
break; break;
} }
togglePlayButton(audioHandler.audioFile.paused ? 'play' : 'pause');
}); });
window.addEventListener('playSong', setActiveOnPlaylist); window.addEventListener('playSong', setActiveOnPlaylist);
window.addEventListener('playSong', e => {
togglePlayButton(audioHandler.audioFile.paused ? 'play' : 'pause');
});
$('.upload-image').addEventListener('click', imageUploader.renderModal.bind(imageUploader)); $('.upload-image').addEventListener('click', imageUploader.renderModal.bind(imageUploader));
body.addDelegatedEventListener('click', '.readAll', e => { body.addDelegatedEventListener('click', '.readAll', forceAllRead);
let playlist = player.playlist.list;
for (let i = 0; i < playlist.length; i++) {
playlist[i].getID3Tag(true);
}
})
body.addDelegatedEventListener('input', '.input-range input[type="range"]', (e, el) => { body.addDelegatedEventListener('input', '.input-range input[type="range"]', (e, el) => {
let current = $('.current', el.parentNode); let current = $('.current', el.parentNode);
@ -112,6 +117,23 @@ async function initHandler() {
break; break;
} }
}) })
$('.help.menu-icon').addEventListener('click', gui.openHelp);
document.onfullscreenchange = e => {
if (body.hasClass('fullscreen')) {
body.removeClass('fullscreen')
} else {
body.addClass('fullscreen')
}
}
}
function forceAllRead() {
let playlist = player.playlist.list;
for (let i = 0; i < playlist.length; i++) {
playlist[i].getID3Tag(true);
}
} }
@ -138,11 +160,17 @@ function setActiveOnPlaylist(e) {
} }
} }
function toggleShuffle() { function toggleShuffle(updateGUI) {
let active = player.playlist.isShuffle; let active = player.playlist.isShuffle;
$('#shuffle').toggleCheck('active', active); if (updateGUI !== false) {
active = !active;
let status = active ? 'enabled' : 'disabled'; let status = active ? 'enabled' : 'disabled';
NotificationHandler.createNotification("Shuffle: " + status, "info", 500); NotificationHandler.createNotification("Shuffle: " + status, "info", 500);
pConf.set("shuffle", active);
pConf.save();
player.playlist.isShuffle = active;
}
$('#shuffle').toggleCheck('active', active);
} }
function togglePlayButton(status) { function togglePlayButton(status) {

View file

@ -13,11 +13,17 @@ class GUI {
'inputs/slider', 'inputs/slider',
'inputs/switch', 'inputs/switch',
'inputs/select', 'inputs/select',
'inputs/option' 'inputs/option',
'help',
]); ]);
this.initDropZone(); this.initDropZone();
} }
openHelp() {
gui.modal.renderModal("Help", template.parseTemplate('help', {}));
gui.modal.showModal();
}
initDropZone() { initDropZone() {
let items = 'drag dragstart dragend dragover dragenter dragleave drop'.split(' '); let items = 'drag dragstart dragend dragover dragenter dragleave drop'.split(' ');
items.forEach(el => { items.forEach(el => {
@ -38,6 +44,7 @@ class GUI {
} }
// create config Inputs from JSON // create config Inputs from JSON
//@todo add support for gui grouping!
class GUIHelper { class GUIHelper {
static fromJSON(json, conf) { static fromJSON(json, conf) {
let data = []; let data = [];
@ -74,7 +81,7 @@ class GUIHelper {
for (let i = 0; i < data.name.length; i++) { for (let i = 0; i < data.name.length; i++) {
let newData = {}; let newData = {};
Object.assign(newData, data); Object.assign(newData, data);
newData.showName = data.name[i]; newData.showName = GUIHelper.richShowName(data, data.name[i].firstUpper());
newData.name = GUIHelper.richName(data, data.props[i]); newData.name = GUIHelper.richName(data, data.props[i]);
content += GUIHelper.createSlider(newData, conf); content += GUIHelper.createSlider(newData, conf);
} }
@ -135,6 +142,13 @@ class GUIHelper {
return name; return name;
} }
static richShowName(data, name) {
if (data.group !== "") {
return data.group.firstUpper() + ' ' + name
}
return name;
}
static createButton(item, conf) { static createButton(item, conf) {
return `<div class='button spaced' data-action="${item.action}">${item.name}</div>` return `<div class='button spaced' data-action="${item.action}">${item.name}</div>`
} }

59
raw/javascript/keys.js Normal file
View file

@ -0,0 +1,59 @@
class KeyHandler {
async init() {
await this.mediaKeys();
await this.addKeyHandler();
window.addEventListener('keydown', this.keyHandler.bind(this));
}
async mediaKeys() {
if ('mediaSession' in navigator) {
let media = navigator.mediaSession;
media.setActionHandler('play', player.playStop.bind(player));
media.setActionHandler('pause', player.playStop.bind(player));
media.setActionHandler('previoustrack', player.prevSong.bind(player));
media.setActionHandler('nexttrack', player.prevSong.bind(player));
}
}
async addKeyHandler() {
eventHandler.addEvent('keys-Space', player.playStop.bind(player));
eventHandler.addEvent('keys-KeyN', player.nextSong.bind(player));
eventHandler.addEvent('keys-KeyV', player.prevSong.bind(player));
eventHandler.addEvent('keys-KeyS', playerConf.open.bind(playerConf));
eventHandler.addEvent('keys-KeyS-shift', toggleShuffle);
eventHandler.addEvent('keys-KeyB', imageUploader.renderModal.bind(imageUploader));
eventHandler.addEvent('keys-KeyF-shift', forceAllRead);
eventHandler.addEvent('keys-KeyH', gui.openHelp);
eventHandler.addEvent('keys-KeyP', e => {
player.playlist.renderPagination(player.playlist.page);
gui.modal.showModal();
});
eventHandler.addEvent('keys-Escape, keys-KeyC-shift', e => {
gui.modal.resetModal();
gui.modal.closeModal();
})
eventHandler.addEvent('keys-F11', e => {
if (document.fullscreenElement) {
document.exitFullscreen().catch(console.error);
} else {
document.body.requestFullscreen().catch(console.error);
}
});
}
async keyHandler(event) {
let key = event.code,
shift = event.shiftKey ? '-shift' : '',
ctrl = event.ctrlKey ? '-ctrl' : '',
name = 'keys-' + key + shift + ctrl;
if (eventHandler.handleEvent({
data: {
cmd: name
}
})) {
event.preventDefault();
event.stopPropagation();
}
}
}

View file

@ -39,7 +39,7 @@ class Notification {
self.type += ' endless'; self.type += ' endless';
} }
self.updateContent(self.message); self.updateContent(self.message);
this.outer.appendChild(self.item); this.outer.prepend(self.item);
if (!endless) { if (!endless) {
setTimeout(this.remove.bind(this), self.time) setTimeout(this.remove.bind(this), self.time)
} }

View file

@ -43,7 +43,7 @@ class Playlist {
this.shuffled = []; this.shuffled = [];
this.index = 0; this.index = 0;
this.page = 0; this.page = 0;
this.isShuffle = false; this.isShuffle = pConf.get("shuffle", false);
$('body').addDelegatedEventListener('change', 'input[type="file"]', this.changeFiles.bind(this)); $('body').addDelegatedEventListener('change', 'input[type="file"]', this.changeFiles.bind(this));
$('body').addDelegatedEventListener('click', '.pagination .item', this.handlePagination.bind(this)); $('body').addDelegatedEventListener('click', '.pagination .item', this.handlePagination.bind(this));
eventHandler.addEvent('id3-request', this.handle.bind(this)); eventHandler.addEvent('id3-request', this.handle.bind(this));

View file

@ -224,6 +224,10 @@ Node.prototype.toggleCheck = function (className, force) {
} }
} }
String.prototype.firstUpper = function () {
return this.charAt(0).toUpperCase() + this.slice(1);
}
File.prototype.toBase64 = function (cb) { File.prototype.toBase64 = function (cb) {
const reader = new FileReader(); const reader = new FileReader();
reader.onloadend = cb; reader.onloadend = cb;

View file

@ -70,8 +70,14 @@ class Wave extends Visual {
prepare(program) { prepare(program) {
this.position = gl.getAttribLocation(program, "a_position"); this.position = gl.getAttribLocation(program, "a_position");
this.color = gl.getUniformLocation(program, "u_color"); this.color = gl.getUniformLocation(program, "u_color");
let lightPos = gl.getUniformLocation(program, "u_lightPos"); let lightPos = gl.getUniformLocation(program, "u_lightPos"),
gl.uniform3fv(lightPos, vConf.get("light", [0, 5, -56])); light = gl.getUniformLocation(program, "u_light");
gl.uniform3fv(lightPos, [
vConf.get("light-x", 0),
vConf.get("light-y", 5),
vConf.get("light-z", -56)
]);
gl.uniform1f(light, parseFloat(vConf.get("light-strength", 0.3)));
} }
afterDraw() { afterDraw() {

View file

@ -164,3 +164,33 @@ modal-footer playlist {
.menus { .menus {
z-index: 10; z-index: 10;
} }
body.fullscreen {
.menus {
display: none;
}
}
.help-list {
display: flex;
flex-direction: column;
.item {
padding: 0 1em;
&:not(:last-child) {
border-bottom: 1px solid #232323;
}
.h2 {
font-size: 1.2em;
margin: 10px 0 5px;
display: block;
}
p {
font-size: .8em;
color: #aaa;
}
}
}

View file

@ -7,6 +7,12 @@ playlist {
position: unset; position: unset;
} }
.playlist-content {
h1 {
padding: 0 1em;
}
}
.pagination { .pagination {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;

View file

@ -1,7 +1,5 @@
#version 300 es #version 300 es
// fragment shaders don't have a default precision so we need
// to pick one. mediump is a good default. It means "medium precision"
precision highp float; precision highp float;
in vec4 fragNormal; in vec4 fragNormal;
in vec3 v_surfaceToLight; in vec3 v_surfaceToLight;

View file

@ -1,7 +1,5 @@
#version 300 es #version 300 es
// fragment shaders don't have a default precision so we need
// to pick one. mediump is a good default. It means "medium precision"
precision mediump float; precision mediump float;
uniform vec4 u_color; uniform vec4 u_color;

View file

@ -1,26 +1,23 @@
#version 300 es #version 300 es
// fragment shaders don't have a default precision so we need
// to pick one. mediump is a good default. It means "medium precision"
precision mediump float; precision mediump float;
in vec4 pos; in vec4 pos;
in vec3 v_surfaceToLight; in vec3 v_surfaceToLight;
in vec3 baseColor; uniform vec3 u_baseColor;
in vec3 maxColor; uniform vec3 u_maxColor;
uniform vec4 u_color; uniform float u_light;
out vec4 outColor; out vec4 outColor;
void main() { void main() {
vec4 fragNormal = normalize(pos); vec4 fragNormal = normalize(pos);
float u_light = 0.3;
float light = max(dot(fragNormal.xyz, normalize(v_surfaceToLight).xyz), u_light); float light = max(dot(fragNormal.xyz, normalize(v_surfaceToLight).xyz), u_light);
float y = pos.z; float y = pos.z;
if (y < 0.0) { if (y < 0.0) {
y = y * -1.0; y = y * -1.0;
} }
vec3 color = mix(baseColor, maxColor, y); vec3 color = mix(u_baseColor, u_maxColor, y);
outColor = vec4(color, 1.0); outColor = vec4(color, 1.0);
outColor.rgb *= light; outColor.rgb *= light;
} }

View file

@ -3,18 +3,12 @@
in vec3 a_position; in vec3 a_position;
uniform mat4 u_matrix; uniform mat4 u_matrix;
uniform vec3 u_lightPos; uniform vec3 u_lightPos;
uniform vec3 u_baseColor;
uniform vec3 u_maxColor;
out vec4 pos; out vec4 pos;
out vec3 v_surfaceToLight; out vec3 v_surfaceToLight;
out vec3 maxColor;
out vec3 baseColor;
void main() { void main() {
pos = u_matrix * vec4(a_position, 1); pos = u_matrix * vec4(a_position, 1);
gl_Position = pos; gl_Position = pos;
v_surfaceToLight = u_lightPos - pos.xyz; v_surfaceToLight = u_lightPos - pos.xyz;
maxColor = u_maxColor;
baseColor = u_baseColor;
} }

View file

@ -1,12 +1,11 @@
#version 300 es #version 300 es
// fragment shaders don't have a default precision so we need
// to pick one. mediump is a good default. It means "medium precision"
precision mediump float; precision mediump float;
in vec4 pos; in vec4 pos;
in vec3 baseColor;
in vec3 maxColor; uniform vec3 u_baseColor;
uniform vec3 u_maxColor;
out vec4 outColor; out vec4 outColor;
void main() { void main() {
@ -14,6 +13,6 @@ void main() {
if (y < 0.0) { if (y < 0.0) {
y = y * -1.0; y = y * -1.0;
} }
vec3 color = mix(baseColor, maxColor, y); vec3 color = mix(u_baseColor, u_maxColor, y);
outColor = vec4(color, 1.0); outColor = vec4(color, 1.0);
} }

View file

@ -2,18 +2,11 @@
in vec3 a_position; in vec3 a_position;
uniform mat4 u_matrix; uniform mat4 u_matrix;
uniform vec3 u_baseColor;
uniform vec3 u_maxColor;
out vec4 pos; out vec4 pos;
out vec3 maxColor;
out vec3 baseColor;
void main() { void main() {
pos = u_matrix * vec4(a_position, 1); pos = u_matrix * vec4(a_position, 1);
pos.y = pos.y * 0.6; pos.y = pos.y * 0.6;
gl_Position = pos; gl_Position = pos;
maxColor = u_maxColor;
baseColor = u_baseColor;
} }