Playlist UI (#2046)
* Test 1 * State 2 * Playlist UI progress * Playlist saving * Playlist saving * Playlist object array * Added Offset to segment options * Positioning * Playlist UI complete
This commit is contained in:
parent
7233c55428
commit
4b46502d22
@ -402,9 +402,8 @@ button {
|
||||
}
|
||||
|
||||
#imgw {
|
||||
width: 200px;
|
||||
height: 55px;
|
||||
display: inline-block;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
#kv, #kn {
|
||||
@ -432,6 +431,12 @@ img {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.wi {
|
||||
image-rendering: pixelated;
|
||||
image-rendering: crisp-edges;
|
||||
width: 210px;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {bottom: 0; opacity: 0;}
|
||||
to {bottom: calc(var(--bh) + 22px); opacity: 1;}
|
||||
@ -519,12 +524,16 @@ input[type=range]:active + .sliderbubble {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sbs {
|
||||
margin: 0px -20px 5px -6px;
|
||||
}
|
||||
|
||||
.sws {
|
||||
width: 212px;
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
.sis {
|
||||
width: 192px !important;
|
||||
width: 214px !important;
|
||||
}
|
||||
|
||||
.hd {
|
||||
@ -561,12 +570,11 @@ input[type=range]:active + .sliderbubble {
|
||||
}
|
||||
|
||||
.btn-s {
|
||||
padding: 9px;
|
||||
width: 276px;
|
||||
background-color: var(--c-2);
|
||||
}
|
||||
.btn-i {
|
||||
padding-bottom: 3px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.btn-icon {
|
||||
margin: 0px 8px 4px 0;
|
||||
@ -578,6 +586,19 @@ input[type=range]:active + .sliderbubble {
|
||||
.btn-p {
|
||||
width: 216px;
|
||||
}
|
||||
.btn-xs {
|
||||
width: 39px;
|
||||
}
|
||||
.btn-pl-add {
|
||||
position: absolute;
|
||||
bottom: -29px;
|
||||
left: 94px;
|
||||
}
|
||||
.btn-pl-del {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
bottom: -3px;
|
||||
}
|
||||
|
||||
#qcs-w {
|
||||
margin-top: 10px;
|
||||
@ -601,6 +622,16 @@ input[type=range]:active + .sliderbubble {
|
||||
width: 42px;
|
||||
}
|
||||
|
||||
.sel-pl {
|
||||
width: 200px;
|
||||
background-position: 176px 16px;
|
||||
}
|
||||
|
||||
.sel-ple {
|
||||
width: 216px;
|
||||
background-position: 192px 16px;
|
||||
}
|
||||
|
||||
select {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
@ -665,6 +696,11 @@ input[type=text] {
|
||||
margin: 26px 0 6px 12px !important;
|
||||
}
|
||||
|
||||
.plentry {
|
||||
margin-top: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stxt {
|
||||
width: 50px !important;
|
||||
}
|
||||
@ -741,9 +777,9 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
|
||||
.cnf-s {
|
||||
position: absolute;
|
||||
top: 66px;
|
||||
right: 28px;
|
||||
padding: 43px 6px;
|
||||
top: 172px;
|
||||
right: 23px;
|
||||
padding: 7.5px 22px;
|
||||
}
|
||||
|
||||
.pwr {
|
||||
@ -1016,6 +1052,16 @@ input[type="text"].search:not(:placeholder-shown) {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.hrz {
|
||||
width: auto;
|
||||
height: 2px;
|
||||
background-color: var(--c-e);
|
||||
}
|
||||
.hrz-pl {
|
||||
margin: 20px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
@ -165,15 +165,7 @@
|
||||
<div id="pcont">
|
||||
Loading...
|
||||
</div><br>
|
||||
<label class="check psvl">
|
||||
Preset cycle
|
||||
<input type="checkbox" id="cyToggle" onchange="toggleCY()">
|
||||
<span class="checkmark psv"></span>
|
||||
</label><br>
|
||||
First preset: <input id="cycs" class="noslide" type="number" min="1" max="249" value="1"><br>
|
||||
Last preset: <input id="cyce" class="noslide" type="number" min="2" max="250" value="3"><br>
|
||||
Time per preset: <input id="cyct" class="noslide" type="number" min="0.2" max="6553.5" step="0.1" value="1.2">s<br>
|
||||
Transition: <input id="cyctt" class="noslide" type="number" min="0" max="65.5" step="0.1" value="0.7">s
|
||||
Transition <input id="tt" class="noslide" type="number" min="0" max="65.5" step="0.1" value="0.7">s
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -189,7 +181,7 @@
|
||||
<div id="namelabel" onclick="toggleNodes()"></div>
|
||||
<div id="info" class="modal">
|
||||
<div id="imgw">
|
||||
<img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAggAAACMCAYAAAAZQlGEAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKLSURBVHhe7dgxjtwwEADBpf//5zUDwklnpzFAnKoSTigNFTT0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGDcOieX+G5nvNLaznil6f1Nv+/tz3c7+3tmen/Tpu/jbe877c85AQD+EQgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAACEQAAAQiAAACEQAIAQCABACAQAINY5+aHvdsYRazvjK9jfM7fvz/0+Y3+/2+336w8CABACAQAIgQAAhEAAAEIgAAAhEACAEAgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAADEOudrfLczXmltZ3yF6fuwv2em9+d+n5ne3zT3cZfp+/AHAQAIgQAAhEAAAEIgAAAhEACAEAgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAACEQAAAYp3zNb7bGUes7Yz8wO334fmeuf35bmd/z9y+v9ufzx8EACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAgBAIAEAIBAAiBAACEQAAAQiAAACEQAIAQCABACAQAIAQCABACAQCIdc4x3+2MV1rbGfmFpr+/6e/l9ue73fT+pt1+H2/bn+/lGX8QAIAQCABACAQAIAQCABACAQAIgQAAhEAAAEIgAAAhEACAEAgAQAgEACAEAgAQAgEACIEAAIRAAABCIAAAIRAAgBAIAEAIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgP/u8/kLYCqAxINTyZkAAAAASUVORK5CYII=">
|
||||
<img class="wi" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAFCAYAAAC5Fuf5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABbSURBVChTlY9bDoAwDMNW7n9nwCipytQN4Z8tbrTHmDmF4oPzyldwRqp1SSdnV/NuZuzqerAByxXznBw3igkeFEfXyUuhK/yFM0CxJfyqXZEOc6/Sr9/bf7uIC5Nwd7orMvAPAAAAAElFTkSuQmCC" />
|
||||
</div><br>
|
||||
<div id="kv">Loading...</div><br>
|
||||
<button class="btn infobtn" onclick="requestJson(null)">Refresh</button>
|
||||
|
@ -14,11 +14,12 @@ var currentPreset = -1;
|
||||
var lastUpdate = 0;
|
||||
var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0;
|
||||
var pcMode = false, pcModeA = false, lastw = 0;
|
||||
var tr = 7;
|
||||
var d = document;
|
||||
const ranges = RangeTouch.setup('input[type="range"]', {});
|
||||
var palettesData;
|
||||
var pJson = {};
|
||||
var pN = "", pI = 0;
|
||||
var pN = "", pI = 0, pNum = 0;
|
||||
var pmt = 1, pmtLS = 0, pmtLast = 0;
|
||||
var lastinfo = {};
|
||||
var cfg = {
|
||||
@ -304,6 +305,10 @@ function pName(i) {
|
||||
return n;
|
||||
}
|
||||
|
||||
function isPlaylist(i) {
|
||||
return pJson[i].playlist && pJson[i].playlist.ps;
|
||||
}
|
||||
|
||||
function papiVal(i) {
|
||||
if (!pJson[i]) return "";
|
||||
var o = Object.assign({},pJson[i]);
|
||||
@ -418,9 +423,9 @@ function populatePresets(fromls)
|
||||
var cn = "";
|
||||
var arr = Object.entries(pJson);
|
||||
arr.sort(cmpP);
|
||||
var added = false;
|
||||
pQL = [];
|
||||
var is = [];
|
||||
pNum = 0;
|
||||
|
||||
for (var key of (arr||[]))
|
||||
{
|
||||
@ -432,15 +437,15 @@ function populatePresets(fromls)
|
||||
|
||||
cn += `<div class="seg pres" id="p${i}o">`;
|
||||
if (cfg.comp.pid) cn += `<div class="pid">${i}</div>`;
|
||||
cn += `<div class="segname pname" onclick="setPreset(${i})">${pName(i)}</div>
|
||||
cn += `<div class="segname pname" onclick="setPreset(${i})">${isPlaylist(i)?"<i class='icons btn-icon'></i>":""}${pName(i)}</div>
|
||||
<i class="icons e-icon flr ${expanded[i+100] ? "exp":""}" id="sege${i+100}" onclick="expand(${i+100})"></i>
|
||||
<div class="segin" id="seg${i+100}"></div>
|
||||
</div><br>`;
|
||||
added = true;
|
||||
pNum++;
|
||||
}
|
||||
|
||||
d.getElementById('pcont').innerHTML = cn;
|
||||
if (added) {
|
||||
if (pNum > 0) {
|
||||
if (pmtLS != pmt && pmt != 0) {
|
||||
localStorage.setItem("wledPmt", pmt);
|
||||
pJson["0"] = {};
|
||||
@ -451,6 +456,7 @@ function populatePresets(fromls)
|
||||
let i = is[a];
|
||||
if (expanded[i+100]) expand(i+100, true);
|
||||
}
|
||||
makePlSel(arr);
|
||||
} else { presetError(true); }
|
||||
updatePA();
|
||||
populateQL();
|
||||
@ -487,8 +493,8 @@ function populateInfo(i)
|
||||
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
|
||||
${inforow("Uptime",getRuntimeStr(i.uptime))}
|
||||
${inforow("Free heap",heap," kB")}
|
||||
${inforow("Estimated current",pwru)}
|
||||
${inforow("Frames / second",i.leds.fps)}
|
||||
${inforow("Estimated current",pwru)}
|
||||
${inforow("Frames / second",i.leds.fps)}
|
||||
${inforow("MAC address",i.mac)}
|
||||
${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB (" +Math.round(i.fs.u*100/i.fs.t) + "%)")}
|
||||
${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")}
|
||||
@ -522,34 +528,39 @@ function populateSegments(s)
|
||||
</div>
|
||||
<i class="icons e-icon flr ${expanded[i] ? "exp":""}" id="sege${i}" onclick="expand(${i})"></i>
|
||||
<div class="segin ${expanded[i] ? "expanded":""}" id="seg${i}">
|
||||
<table class="segt">
|
||||
<tr>
|
||||
<td class="segtd">Start LED</td>
|
||||
<td class="segtd">Stop LED</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${ledCount-1}" value="${inst.start}" oninput="updateLen(${i})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${ledCount}" value="${inst.stop}" oninput="updateLen(${i})"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="segt">
|
||||
<tr>
|
||||
<td class="segtd">Grouping</td>
|
||||
<td class="segtd">Spacing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="h bp" id="seg${i}len"></div>
|
||||
<i class="icons e-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})"></i>
|
||||
<div class="sliderwrap il sws">
|
||||
<input id="seg${i}bri" class="noslide sis" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<i class="icons e-icon cnf cnf-s" id="segc${i}" onclick="setSeg(${i})"></i>
|
||||
<i class="icons e-icon del" id="segd${i}" onclick="delSeg(${i})"></i>
|
||||
<div class="sbs">
|
||||
<i class="icons e-icon pwr ${powered[i] ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})"></i>
|
||||
<div class="sliderwrap il sws">
|
||||
<input id="seg${i}bri" class="noslide sis" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="infot">
|
||||
<tr>
|
||||
<td class="segtd">Start LED</td>
|
||||
<td class="segtd">Stop LED</td>
|
||||
<td class="segtd">Offset</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${ledCount-1}" value="${inst.start}" oninput="updateLen(${i})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${ledCount}" value="${inst.stop}" oninput="updateLen(${i})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}of" type="number" min="0" max="${ledCount-1}" value="${inst.of}" oninput="updateLen(${i})"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="infot">
|
||||
<tr>
|
||||
<td class="segtd">Grouping</td>
|
||||
<td class="segtd">Spacing</td>
|
||||
<td class="segtd">Apply</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})"></td>
|
||||
<td class="segtd"><input class="noslide segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})"></td>
|
||||
<td class="segtd"><i class="icons e-icon cnf cnf-s" id="segc${i}" onclick="setSeg(${i})"></i></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="h" id="seg${i}len"></div>
|
||||
<button class="btn btn-i btn-xs del" id="segd${i}" onclick="delSeg(${i})"><i class="icons btn-icon"></i></button>
|
||||
<label class="check revchkl">
|
||||
Reverse direction
|
||||
<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev ? "checked":""}>
|
||||
@ -1007,11 +1018,8 @@ function requestJson(command, rinfo = true, verbose = true) {
|
||||
nlFade = s.nl.fade;
|
||||
syncSend = s.udpn.send;
|
||||
currentPreset = s.ps;
|
||||
d.getElementById('cyToggle').checked = (s.pl >= 0);
|
||||
d.getElementById('cycs').value = s.ccnf.min;
|
||||
d.getElementById('cyce').value = s.ccnf.max;
|
||||
d.getElementById('cyct').value = s.ccnf.time /10;
|
||||
d.getElementById('cyctt').value = s.transition /10;
|
||||
tr = s.transition;
|
||||
d.getElementById('tt').value = tr/10;
|
||||
|
||||
var selc=0; var ind=0;
|
||||
populateSegments(s);
|
||||
@ -1043,7 +1051,7 @@ function requestJson(command, rinfo = true, verbose = true) {
|
||||
var selFx = e1.querySelector(`input[name="fx"][value="${i.fx}"]`);
|
||||
if (selFx) selFx.checked = true;
|
||||
else location.reload(); //effect list is gone (e.g. if restoring tab). Reload.
|
||||
|
||||
|
||||
var selElement = e1.querySelector('.selected');
|
||||
if (selElement) {
|
||||
selElement.classList.remove('selected')
|
||||
@ -1077,7 +1085,7 @@ function requestJson(command, rinfo = true, verbose = true) {
|
||||
errstr = "Not enough space to save preset!";
|
||||
break;
|
||||
case 12:
|
||||
errstr = "The requested preset does not exist.";
|
||||
errstr = "Preset not found.";
|
||||
break;
|
||||
case 19:
|
||||
errstr = "A filesystem error has occured.";
|
||||
@ -1096,7 +1104,7 @@ function requestJson(command, rinfo = true, verbose = true) {
|
||||
function togglePower() {
|
||||
isOn = !isOn;
|
||||
var obj = {"on": isOn};
|
||||
obj.transition = parseInt(d.getElementById('cyctt').value*10);
|
||||
obj.transition = parseInt(d.getElementById('tt').value*10);
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
@ -1172,7 +1180,7 @@ function makeSeg() {
|
||||
<td class="segtd"><input class="noslide segn" id="seg${lowestUnused}e" type="number" min="0" max="${ledCount}" value="${ledCount}" oninput="updateLen(${lowestUnused})"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="h" id="seg${lowestUnused}len">${ledCount - ns} LEDs</div>
|
||||
<div class="h" id="seg${lowestUnused}len">${ledCount - ns} LED${ledCount - ns >1 ? "s":""}</div>
|
||||
<i class="icons e-icon cnf cnf-s half" id="segc${lowestUnused}" onclick="setSeg(${lowestUnused}); resetUtil();"></i>
|
||||
</div>
|
||||
</div>`;
|
||||
@ -1184,37 +1192,148 @@ function resetUtil() {
|
||||
d.getElementById('segutil').innerHTML = cn;
|
||||
}
|
||||
|
||||
function makeP(i) {
|
||||
var plJson = {"0":{
|
||||
"ps": [0],
|
||||
"dur": [100],
|
||||
"transition": [-1], //to be inited to default transition dur
|
||||
"repeat": 0,
|
||||
"r": false,
|
||||
"end": 0
|
||||
}};
|
||||
|
||||
var plSelContent = "";
|
||||
function makePlSel(arr) {
|
||||
plSelContent = "";
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var n = arr[i][1].n ? arr[i][1].n : "Preset " + arr[i][0];
|
||||
if (arr[i][1].playlist && arr[i][1].playlist.ps) continue; //remove playlists, sub-playlists not yet supported
|
||||
plSelContent += `<option value=${arr[i][0]}>${n}</option>`
|
||||
}
|
||||
}
|
||||
|
||||
function refreshPlE(p) {
|
||||
var plEDiv = d.getElementById(`ple${p}`);
|
||||
if (!plEDiv) return;
|
||||
var content = "";
|
||||
for (var i = 0; i < plJson[p].ps.length; i++) {
|
||||
content += makePlEntry(p,i);
|
||||
}
|
||||
plEDiv.innerHTML = content;
|
||||
var dels = plEDiv.getElementsByClassName("btn-pl-del");
|
||||
if (dels.length < 2 && p > 0) dels[0].style.display = "none";
|
||||
|
||||
var sels = d.getElementById(`seg${p+100}`).getElementsByClassName("sel");
|
||||
for (var i of sels) {
|
||||
if (i.dataset.val) {
|
||||
if (parseInt(i.dataset.val) > 0) i.value = i.dataset.val;
|
||||
else plJson[p].ps[i.dataset.index] = parseInt(i.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//p: preset ID, i: ps index
|
||||
function addPl(p,i) {
|
||||
plJson[p].ps.splice(i+1,0,0);
|
||||
plJson[p].dur.splice(i+1,0,plJson[p].dur[i]);
|
||||
plJson[p].transition.splice(i+1,0,plJson[p].transition[i]);
|
||||
refreshPlE(p);
|
||||
}
|
||||
|
||||
function delPl(p,i) {
|
||||
if (plJson[p].ps.length < 2) {if (p == 0) resetPUtil(); return;}
|
||||
plJson[p].ps.splice(i,1);
|
||||
plJson[p].dur.splice(i,1);
|
||||
plJson[p].transition.splice(i,1);
|
||||
refreshPlE(p);
|
||||
}
|
||||
|
||||
function plePs(p,i,field) {
|
||||
plJson[p].ps[i] = parseInt(field.value);
|
||||
}
|
||||
|
||||
function pleDur(p,i,field) {
|
||||
if (field.validity.valid)
|
||||
plJson[p].dur[i] = Math.floor(field.value*10);
|
||||
}
|
||||
|
||||
function pleTr(p,i,field) {
|
||||
if (field.validity.valid)
|
||||
plJson[p].transition[i] = Math.floor(field.value*10);
|
||||
}
|
||||
|
||||
function plR(p) {
|
||||
var pl = plJson[p];
|
||||
pl.r = d.getElementById(`pl${p}rtgl`).checked;
|
||||
if (d.getElementById(`pl${p}rptgl`).checked) { //infinite
|
||||
pl.repeat = 0;
|
||||
delete pl.end;
|
||||
d.getElementById(`pl${p}o1`).style.display = "none";
|
||||
} else {
|
||||
pl.repeat = parseInt(d.getElementById(`pl${p}rp`).value);
|
||||
pl.end = parseInt(d.getElementById(`pl${p}selEnd`).value);
|
||||
d.getElementById(`pl${p}o1`).style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
function makeP(i,pl) {
|
||||
var content = "";
|
||||
if (pl) {
|
||||
var rep = plJson[i].repeat ? plJson[i].repeat : 0;
|
||||
content = `
|
||||
<div id="ple${i}"></div><label class="check revchkl">
|
||||
Shuffle
|
||||
<input type="checkbox" id="pl${i}rtgl" onchange="plR(${i})" ${plJson[i].r?"checked":""}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<label class="check revchkl">
|
||||
Repeat indefinitely
|
||||
<input type="checkbox" id="pl${i}rptgl" onchange="plR(${i})" ${rep?"":"checked"}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<div id="pl${i}o1" style="display:${rep?"block":"none"}">
|
||||
<div class="c">Repeat <input class="noslide" type="number" id="pl${i}rp" oninput="plR(${i})" max=127 min=0 value=${rep>0?rep:1}> times</div>
|
||||
End preset:<br>
|
||||
<select class="btn sel sel-ple" id="pl${i}selEnd" onchange="plR(${i})" data-val=${plJson[i].end?plJson[i].end:0}>
|
||||
<option value=0>None</option>
|
||||
${plSelContent}
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-i btn-p" onclick="testPl(${i}, this)"><i class='icons btn-icon'></i>Test</button>`;
|
||||
}
|
||||
else content = `<label class="check revchkl">
|
||||
Include brightness
|
||||
<input type="checkbox" id="p${i}ibtgl" checked>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<label class="check revchkl">
|
||||
Save segment bounds
|
||||
<input type="checkbox" id="p${i}sbtgl" checked>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>`;
|
||||
|
||||
return `
|
||||
<input type="text" class="ptxt noslide" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/><br>
|
||||
<div class="c">Quick load label: <input type="text" class="stxt noslide" maxlength=2 value="${qlName(i)}" id="p${i}ql" autocomplete="off"/></div>
|
||||
<div class="h">(leave empty for no Quick load button)</div>
|
||||
<div ${pl&&i==0?"style='display:none'":""}>
|
||||
<label class="check revchkl">
|
||||
${(i>0)?"Overwrite with state":"Use current state"}
|
||||
<input type="checkbox" id="p${i}cstgl" onchange="tglCs(${i})" ${(i>0)?"":"checked"}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label><br>
|
||||
<div class="po2" id="p${i}o2">
|
||||
API command<br>
|
||||
<textarea class="noslide" id="p${i}api"></textarea>
|
||||
</div>
|
||||
<div class="po1" id="p${i}o1">
|
||||
<label class="check revchkl">
|
||||
Include brightness
|
||||
<input type="checkbox" id="p${i}ibtgl" checked>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
<label class="check revchkl">
|
||||
Save segment bounds
|
||||
<input type="checkbox" id="p${i}sbtgl" checked>
|
||||
<span class="checkmark schk"></span>
|
||||
</label>
|
||||
${pl?"Show playlist editor":(i>0)?"Overwrite with state":"Use current state"}
|
||||
<input type="checkbox" id="p${i}cstgl" onchange="tglCs(${i})" ${(i==0||pl)?"checked":""}>
|
||||
<span class="checkmark schk"></span>
|
||||
</label><br>
|
||||
</div>
|
||||
<div class="po2" id="p${i}o2">
|
||||
API command<br>
|
||||
<textarea class="noslide" id="p${i}api"></textarea>
|
||||
</div>
|
||||
<div class="po1" id="p${i}o1">
|
||||
${content}
|
||||
</div>
|
||||
<div class="c">Save to ID <input class="noslide" id="p${i}id" type="number" oninput="checkUsed(${i})" max=250 min=1 value=${(i>0)?i:getLowestUnusedP()}></div>
|
||||
<div class="c">
|
||||
<button class="btn btn-i btn-p" onclick="saveP(${i})"><i class="icons btn-icon"></i>${(i>0)?"Save changes":"Save preset"}</button>
|
||||
${(i>0)?'<button class="btn btn-i btn-p" onclick="delP('+i+')"><i class="icons btn-icon"></i>Delete preset</button>':
|
||||
'<button class="btn btn-p" onclick="resetPUtil()">Cancel</button>'}
|
||||
<button class="btn btn-i btn-p" onclick="saveP(${i},${pl})"><i class="icons btn-icon"></i>Save ${(pl)?"playlist":(i>0)?"changes":"preset"}</button>
|
||||
${(i>0)?'<button class="btn btn-i btn-p" id="p'+i+'del" onclick="delP('+i+')"><i class="icons btn-icon"></i>Delete '+(pl?"playlist":"preset"):
|
||||
'<button class="btn btn-p" onclick="resetPUtil()">Cancel'}</button>
|
||||
</div>
|
||||
<div class="pwarn ${(i>0)?"bp":""} c" id="p${i}warn">
|
||||
|
||||
@ -1228,11 +1347,48 @@ function makePUtil() {
|
||||
New preset</div>
|
||||
<div class="segin expanded">
|
||||
${makeP(0)}</div></div>`;
|
||||
updateTrail(d.getElementById('p0p'));
|
||||
}
|
||||
|
||||
function makePlEntry(p,i) {
|
||||
return `
|
||||
<div class="plentry">
|
||||
${i+1}:
|
||||
<select class="btn sel sel-pl" onchange="plePs(${p},${i},this)" data-val=${plJson[p].ps[i]} data-index=${i}>
|
||||
${plSelContent}
|
||||
</select>
|
||||
<table class="segt">
|
||||
<tr>
|
||||
<td class="segtd">Duration</td>
|
||||
<td class="segtd">Transition</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="segtd"><input class="noslide segn" type="number" max=6553.0 min=0.2 step=0.1 oninput="pleDur(${p},${i},this)" value=${plJson[p].dur[i]/10.0}></td>
|
||||
<td class="segtd"><input class="noslide segn" type="number" max=65.0 min=0.0 step=0.1 oninput="pleTr(${p},${i},this)" value=${plJson[p].transition[i]/10.0}> s</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button class="btn btn-i btn-xs btn-pl-del" onclick="delPl(${p},${i})"><i class="icons btn-icon"></i></button></div>
|
||||
<div class="hrz hrz-pl" />
|
||||
<button class="btn btn-i btn-xs btn-pl-add" onclick="addPl(${p},${i})"><i class="icons btn-icon"></i></button></div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function makePlUtil() {
|
||||
if (pNum < 2) {
|
||||
showToast("You need at least 2 presets to make a playlist!"); return;
|
||||
}
|
||||
if (plJson[0].transition[0] < 0) plJson[0].transition[0] = tr;
|
||||
d.getElementById('putil').innerHTML = `<div class="seg pres">
|
||||
<div class="segname newseg">
|
||||
New playlist</div>
|
||||
<div class="segin expanded" id="seg100">
|
||||
${makeP(0,true)}</div></div>`;
|
||||
|
||||
refreshPlE(0);
|
||||
}
|
||||
|
||||
function resetPUtil() {
|
||||
var cn = `<button class="btn btn-s btn-i" onclick="makePUtil()"><i class="icons btn-icon"></i>Create preset</button><br>`;
|
||||
var cn = `<button class="btn btn-s btn-i" onclick="makePUtil()"><i class="icons btn-icon"></i>Create preset</button><br>
|
||||
<button class="btn btn-s btn-i" onclick="makePlUtil()"><i class='icons btn-icon'></i>Create playlist</button><br>`;
|
||||
d.getElementById('putil').innerHTML = cn;
|
||||
}
|
||||
|
||||
@ -1266,8 +1422,10 @@ function setSeg(s){
|
||||
{
|
||||
var grp = parseInt(d.getElementById(`seg${s}grp`).value);
|
||||
var spc = parseInt(d.getElementById(`seg${s}spc`).value);
|
||||
var ofs = parseInt(d.getElementById(`seg${s}of` ).value);
|
||||
obj.seg.grp = grp;
|
||||
obj.seg.spc = spc;
|
||||
obj.seg.of = ofs;
|
||||
}
|
||||
requestJson(obj);
|
||||
}
|
||||
@ -1339,7 +1497,7 @@ function setPalette(paletteId = null)
|
||||
|
||||
function setBri() {
|
||||
var obj = {"bri": parseInt(d.getElementById('sliderBri').value)};
|
||||
obj.transition = parseInt(d.getElementById('cyctt').value*10);
|
||||
obj.transition = parseInt(d.getElementById('tt').value*10);
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
@ -1358,17 +1516,6 @@ function setLor(i) {
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
function toggleCY() {
|
||||
var obj = {"pl" : -1};
|
||||
if (d.getElementById('cyToggle').checked)
|
||||
{
|
||||
obj = {"pl": 0, "ccnf": {"min": parseInt(d.getElementById('cycs').value), "max": parseInt(d.getElementById('cyce').value), "time": parseInt(d.getElementById('cyct').value*10)}};
|
||||
obj.transition = parseInt(d.getElementById('cyctt').value*10);
|
||||
}
|
||||
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
function setPreset(i) {
|
||||
var obj = {"ps": i};
|
||||
|
||||
@ -1377,12 +1524,14 @@ function setPreset(i) {
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
function saveP(i) {
|
||||
function saveP(i,pl) {
|
||||
pI = parseInt(d.getElementById(`p${i}id`).value);
|
||||
if (!pI || pI < 1) pI = (i>0) ? i : getLowestUnusedP();
|
||||
pN = d.getElementById(`p${i}txt`).value;
|
||||
if (pN == "") pN = "Preset " + pI;
|
||||
|
||||
if (pN == "") pN = (pl?"Playlist ":"Preset ") + pI;
|
||||
var obj = {};
|
||||
|
||||
if (!d.getElementById(`p${i}cstgl`).checked) {
|
||||
var raw = d.getElementById(`p${i}api`).value;
|
||||
try {
|
||||
@ -1396,15 +1545,21 @@ function saveP(i) {
|
||||
d.getElementById(`p${i}warn`).innerHTML = "⚠ Syntax error in custom JSON API command";
|
||||
return;
|
||||
} else if (raw.indexOf("Please") == 0) {
|
||||
d.getElementById(`p${i}warn`).innerHTML = "⚠ Please refresh the page before modifying this preset";
|
||||
d.getElementById(`p${i}warn`).innerHTML = "⚠ Please refresh the page before modifying this preset";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
obj.o = true;
|
||||
} else {
|
||||
obj.ib = d.getElementById(`p${i}ibtgl`).checked;
|
||||
obj.sb = d.getElementById(`p${i}sbtgl`).checked;
|
||||
if (pl) {
|
||||
obj.playlist = plJson[i];
|
||||
obj.o = true;
|
||||
} else {
|
||||
obj.ib = d.getElementById(`p${i}ibtgl`).checked;
|
||||
obj.sb = d.getElementById(`p${i}sbtgl`).checked;
|
||||
}
|
||||
}
|
||||
|
||||
obj.psave = pI; obj.n = pN;
|
||||
var pQN = d.getElementById(`p${i}ql`).value;
|
||||
if (pQN.length > 0) obj.ql = pQN;
|
||||
@ -1426,11 +1581,36 @@ function saveP(i) {
|
||||
resetPUtil();
|
||||
}
|
||||
|
||||
function delP(i) {
|
||||
var obj = {"pdel": i};
|
||||
function testPl(i,bt) {
|
||||
if (bt.dataset.test == 1) {
|
||||
bt.dataset.test = 0;
|
||||
bt.innerHTML = "<i class='icons btn-icon'></i>Test";
|
||||
stopPl();
|
||||
return;
|
||||
}
|
||||
bt.dataset.test = 1;
|
||||
bt.innerHTML = "<i class='icons btn-icon'></i>Stop";
|
||||
var obj = {};
|
||||
obj.playlist = plJson[i];
|
||||
requestJson(obj);
|
||||
delete pJson[i];
|
||||
populatePresets();
|
||||
}
|
||||
|
||||
function stopPl() {
|
||||
requestJson({playlist:{}})
|
||||
}
|
||||
|
||||
function delP(i) {
|
||||
var bt = d.getElementById(`p${i}del`);
|
||||
if (bt.dataset.cnf == 1) {
|
||||
var obj = {"pdel": i};
|
||||
requestJson(obj);
|
||||
delete pJson[i];
|
||||
populatePresets();
|
||||
} else {
|
||||
bt.style.color = "#f00";
|
||||
bt.innerHTML = "<i class='icons btn-icon'></i>Confirm delete";
|
||||
bt.dataset.cnf = 1;
|
||||
}
|
||||
}
|
||||
|
||||
function selectSlot(b) {
|
||||
@ -1530,7 +1710,7 @@ function setColor(sr) {
|
||||
}
|
||||
updateHex();
|
||||
updateRgb();
|
||||
obj.transition = parseInt(d.getElementById('cyctt').value*10);
|
||||
obj.transition = parseInt(d.getElementById('tt').value*10);
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
@ -1658,22 +1838,61 @@ function cancelSearch(ic) {
|
||||
searchField.focus();
|
||||
}
|
||||
|
||||
//make sure "dur" and "transition" are arrays with at least the length of "ps"
|
||||
function formatArr(pl) {
|
||||
var l = pl.ps.length;
|
||||
if (!Array.isArray(pl.dur)) {
|
||||
var v = pl.dur;
|
||||
if (isNaN(v)) v = 100;
|
||||
pl.dur = [v];
|
||||
}
|
||||
var l2 = pl.dur.length;
|
||||
if (l2 < l)
|
||||
{
|
||||
for (var i = 0; i < l - l2; i++)
|
||||
pl.dur.push(pl.dur[l2-1]);
|
||||
}
|
||||
|
||||
if (!Array.isArray(pl.transition)) {
|
||||
var v = pl.transition;
|
||||
if (isNaN(v)) v = tr;
|
||||
pl.transition = [v];
|
||||
}
|
||||
var l2 = pl.transition.length;
|
||||
if (l2 < l)
|
||||
{
|
||||
for (var i = 0; i < l - l2; i++)
|
||||
pl.transition.push(pl.transition[l2-1]);
|
||||
}
|
||||
}
|
||||
|
||||
function expand(i,a)
|
||||
{
|
||||
if (!a) expanded[i] = !expanded[i];
|
||||
d.getElementById('seg' +i).style.display = (expanded[i]) ? "block":"none";
|
||||
d.getElementById('sege' +i).style.transform = (expanded[i]) ? "rotate(180deg)":"rotate(0deg)";
|
||||
if (i > 100) { //presets
|
||||
var p = i-100;
|
||||
d.getElementById(`p${p}o`).style.background = (expanded[i] || p != currentPreset)?"var(--c-2)":"var(--c-6)";
|
||||
if (d.getElementById('seg' +i).innerHTML == "") {
|
||||
d.getElementById('seg' +i).innerHTML = makeP(p);
|
||||
var papi = papiVal(p);
|
||||
d.getElementById(`p${p}api`).value = papi;
|
||||
if (papi.indexOf("Please") == 0) d.getElementById(`p${p}cstgl`).checked = true;
|
||||
tglCs(p);
|
||||
}
|
||||
if (i < 100) return; //no preset, we are done
|
||||
|
||||
var p = i-100;
|
||||
d.getElementById(`p${p}o`).style.background = (expanded[i] || p != currentPreset)?"var(--c-2)":"var(--c-6)";
|
||||
if (d.getElementById('seg' +i).innerHTML != "") return;
|
||||
if (isPlaylist(p)) {
|
||||
plJson[p] = pJson[p].playlist;
|
||||
//make sure all keys are present in plJson[p]
|
||||
formatArr(plJson[p]);
|
||||
if (isNaN(plJson[p].repeat)) plJson[p].repeat = 0;
|
||||
if (!plJson[p].r) plJson[p].r = false;
|
||||
if (isNaN(plJson[p].end)) plJson[p].end = 0;
|
||||
|
||||
d.getElementById('seg' +i).innerHTML = makeP(p,true);
|
||||
refreshPlE(p);
|
||||
} else {
|
||||
d.getElementById('seg' +i).innerHTML = makeP(p);
|
||||
}
|
||||
var papi = papiVal(p);
|
||||
d.getElementById(`p${p}api`).value = papi;
|
||||
if (papi.indexOf("Please") == 0) d.getElementById(`p${p}cstgl`).checked = true;
|
||||
tglCs(p);
|
||||
}
|
||||
|
||||
function unfocusSliders() {
|
||||
|
Loading…
Reference in New Issue
Block a user