Merge remote-tracking branch 'upstream/main' into mdev

This commit is contained in:
Ewoud
2023-03-21 11:39:30 +01:00
22 changed files with 4116 additions and 3966 deletions

View File

@@ -4,6 +4,7 @@ var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, sy
var hasWhite = false, hasRGB = false, hasCCT = false;
var nlDur = 60, nlTar = 0;
var nlMode = false;
var segLmax = 0; // size (in pixels) of largest selected segment
var selectedFx = 0;
var selectedPal = 0;
var csel = 0; // selected color slot (0-2)
@@ -63,13 +64,10 @@ function setCSL(cs)
let w = cs.dataset.w ? parseInt(cs.dataset.w) : 0;
let hasShadow = getComputedStyle(cs).textShadow !== "none";
if (hasRGB && !isRgbBlack(cs.dataset)) {
cs.style.backgroundColor = rgbStr(cs.dataset);
if (!hasShadow) cs.style.color = rgbBri(cs.dataset) > 127 ? "#000":"#fff"; // if text has no CSS "shadow"
if (hasWhite && w > 0) {
cs.style.background = `linear-gradient(180deg, ${rgbStr(cs.dataset)} 30%, rgb(${w},${w},${w}))`;
}
cs.style.background = (hasWhite && w > 0) ? `linear-gradient(180deg, ${rgbStr(cs.dataset)} 30%, rgb(${w},${w},${w}))` : rgbStr(cs.dataset);
} else {
if (!hasWhite) w = 0;
if (hasRGB && !hasWhite) w = 0;
cs.style.background = `rgb(${w},${w},${w})`;
if (!hasShadow) cs.style.color = w > 127 ? "#000":"#fff";
}
@@ -779,8 +777,7 @@ function populateSegments(s)
<i class="icons e-icon flr" id="sege${i}" onclick="expand(${i})">&#xe395;</i>
${cfg.comp.segpwr?segp:''}
<div class="segin" id="seg${i}in">
<input id="seg${i}fx" value="${inst.fx}" type="hidden"/>
<input type="text" class="ptxt noslide" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/>
<input type="text" class="ptxt" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/>
<table class="infot segt">
<tr>
<td>${isM&&staX<mw*mh?'Start X':'Start LED'}</td>
@@ -788,24 +785,24 @@ function populateSegments(s)
<td>${isM&&staX<mw*mh?'':'Offset'}</td>
</tr>
<tr>
<td><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)-1}" value="${staX}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)}" value="${stoX-(cfg.comp.seglen?staX:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td style="text-align:revert;">${isM&&staX<mw*mh?miXck+'<br>'+rvXck:''}<input class="noslide segn ${isM&&staX<mw*mh?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
<td><input class="segn" id="seg${i}s" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)-1}" value="${staX}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="segn" id="seg${i}e" type="number" min="0" max="${(isM&&staX<mw*mh?mw:ledCount)}" value="${stoX-(cfg.comp.seglen?staX:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td style="text-align:revert;">${isM&&staX<mw*mh?miXck+'<br>'+rvXck:''}<input class="segn ${isM&&staX<mw*mh?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
</tr>
${isM&&staX<mw*mh ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td><td></td></tr>'+
'<tr>'+
'<td><input class="noslide segn" id="seg'+i+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+staY+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td><input class="noslide segn" id="seg'+i+'eY" type="number" min="0" max="'+mh+'" value="'+(stoY-(cfg.comp.seglen?staY:0))+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td><input class="segn" id="seg'+i+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+staY+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td><input class="segn" id="seg'+i+'eY" type="number" min="0" max="'+mh+'" value="'+(stoY-(cfg.comp.seglen?staY:0))+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td style="text-align:revert;">'+miYck+'<br>'+rvYck+'</td>'+
'</tr>':''}
<tr>
<td>Grouping</td>
<td>Spacing</td>
<td><!--Apply--></td>
<td></td>
</tr>
<tr>
<td><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="noslide segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="segn" id="seg${i}spc" type="number" min="0" max="255" value="${inst.spc}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td style="text-align:revert;"><button class="btn btn-xs" onclick="setSeg(${i})"><i class="icons btn-icon" id="segc${i}">&#xe390;</i></button></td>
</tr>
</table>
@@ -836,10 +833,13 @@ function populateSegments(s)
for (var i = 0; i <= lSeg; i++) {
updateLen(i, false); //WLEDMM: no draw
updateTrail(gId(`seg${i}bri`));
gId(`segr${i}`).style.display = "none";
gId(`segr${i}`).classList.add("hide");
if (!gId(`seg${i}sel`).checked && gId('selall')) gId('selall').checked = false; // uncheck if at least one is unselected.
}
if (segCount < 2) gId(`segd${lSeg}`).style.display = "none";
if (segCount < 2) {
gId(`segd${lSeg}`).classList.add("hide");
gId(`segp0`).classList.add("hide");
}
if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline";
gId('segutil2').style.display = (segCount > 1) ? "block":"none"; // rsbtn parent
@@ -888,6 +888,7 @@ function populateEffects()
let m = (eP.length<4 || eP[3]==='')?'1':eP[3]; // flags
if (id == 0) m = ''; // solid has no flags
if (m.length>0) {
if (m.includes('0')) nm += "&#8226;"; // 0D effects (PWM & On/Off)
if (m.includes('1')) nm += "&#8942;"; // 1D effects
if (m.includes("1.5d")) nm += "&#8942;"; // WLEDMM: vStrips
if (m.includes('2')) nm += "&#9638;"; // 2D effects
@@ -1440,18 +1441,19 @@ function updateUI()
}
if (hasWhite) updateTrail(gId('sliderW'));
gId('wwrap').style.display = (hasWhite) ? "block":"none"; // white channel
gId('wbal').style.display = (hasCCT) ? "block":"none"; // white balance
var ccfg = cfg.comp.colors;
gId('hexw').style.display = ccfg.hex ? "block":"none"; // HEX input
gId('picker').style.display = (hasRGB && ccfg.picker) ? "block":"none"; // color picker wheel
gId('hwrap').style.display = (hasRGB && !ccfg.picker) ? "block":"none"; // color picker wheel
gId('swrap').style.display = (hasRGB && !ccfg.picker) ? "block":"none"; // color picker wheel
gId('vwrap').style.display = (hasRGB /*&& ccfg.picker*/) ? "block":"none"; // brightness (value) slider
gId('kwrap').style.display = (hasRGB && !hasCCT /*&& ccfg.picker*/) ? "block":"none"; // Kelvin slider
gId('rgbwrap').style.display = (hasRGB && ccfg.rgb) ? "block":"none"; // RGB sliders
gId('qcs-w').style.display = (hasRGB && ccfg.quick) ? "block":"none"; // quick selection
//gId('palw').style.display = hasRGB ? "block":"none"; // palettes
gId('wwrap').style.display = (hasWhite) ? "block":"none"; // white channel
gId('wbal').style.display = (hasCCT) ? "block":"none"; // white balance
gId('hexw').style.display = (ccfg.hex) ? "block":"none"; // HEX input
gId('picker').style.display = (hasRGB && ccfg.picker) ? "block":"none"; // color picker wheel
gId('hwrap').style.display = (hasRGB && !ccfg.picker) ? "block":"none"; // hue slider
gId('swrap').style.display = (hasRGB && !ccfg.picker) ? "block":"none"; // saturation slider
gId('vwrap').style.display = (hasRGB) ? "block":"none"; // brightness (value) slider
gId('kwrap').style.display = (hasRGB && !hasCCT) ? "block":"none"; // Kelvin slider
gId('rgbwrap').style.display = (hasRGB && ccfg.rgb) ? "block":"none"; // RGB sliders
gId('qcs-w').style.display = (hasRGB && ccfg.quick) ? "block":"none"; // quick selection
//gId('csl').style.display = (hasRGB || hasWhite) ? "block":"none"; // color selectors (hide for On/Off bus)
//gId('palw').style.display = (hasRGB) ? "inline-block":"none"; // palettes are shown/hidden in setEffectParameters()
updatePA();
updatePSliders();
@@ -1496,7 +1498,19 @@ function updateSelectedFx()
if (selectedEffect) {
selectedEffect.classList.add('selected');
setEffectParameters(selectedFx);
// hide non-0D effects if segment only has 1 pixel (0D)
var fxs = parent.querySelectorAll('.lstI');
for (const fx of fxs) {
let opts = fx.dataset.opt.split(";");
if (fx.dataset.id>0) {
if (segLmax==0) fx.classList.add('hide'); // none of the segments selected (hide all effects)
else {
if (segLmax==1 && (!opts[3] || opts[3].indexOf("0")<0)) fx.classList.add('hide');
else fx.classList.remove('hide');
}
}
}
// hide 2D mapping and/or sound simulation options
var selectedName = selectedEffect.querySelector(".lstIname").innerText;
var segs = gId("segcont").querySelectorAll(`div[data-map="map2D"]`);
for (const seg of segs) if (selectedName.indexOf("\u25A6")<0) seg.classList.remove('hide'); else seg.classList.add('hide');
@@ -1584,6 +1598,7 @@ function readState(s,command=false)
var selc=0;
var sellvl=0; // 0: selc is invalid, 1: selc is mainseg, 2: selc is first selected
hasRGB = hasWhite = hasCCT = false;
segLmax = 0;
for (let i = 0; i < (s.seg||[]).length; i++)
{
if (sellvl == 0 && s.seg[i].id == s.mainseg) {
@@ -1597,6 +1612,8 @@ function readState(s,command=false)
hasRGB |= !!(lc & 0x01);
hasWhite |= !!(lc & 0x02);
hasCCT |= !!(lc & 0x04);
let sLen = (s.seg[i].stop - s.seg[i].start)*(s.seg[i].stopY?(s.seg[i].stopY - s.seg[i].startY):1);
segLmax = segLmax < sLen ? sLen : segLmax;
}
}
var i=s.seg[selc];
@@ -1617,7 +1634,7 @@ function readState(s,command=false)
cd[e].dataset.r = i.col[e][0];
cd[e].dataset.g = i.col[e][1];
cd[e].dataset.b = i.col[e][2];
if (hasWhite) { cd[e].dataset.w = i.col[e][3]; }
if (hasWhite || (!hasRGB && !hasWhite)) { cd[e].dataset.w = i.col[e][3]; }
setCSL(cd[e]);
}
selectSlot(csel);
@@ -1769,8 +1786,8 @@ function setEffectParameters(idx)
var palw = gId("palw"); // wrapper
var pall = gId("pall"); // label
// if not controlDefined or palette has a value
if ((!controlDefined) || (paOnOff.length>0 && paOnOff[0]!="" && isNaN(paOnOff[0]))) {
palw.style.display = hasRGB ? "inline-block" : "none";
if (hasRGB && ((!controlDefined) || (paOnOff.length>0 && paOnOff[0]!="" && isNaN(paOnOff[0])))) {
palw.style.display = "inline-block";
if (paOnOff.length>0 && paOnOff[0].indexOf("=")>0) {
// embeded default values
var dPos = paOnOff[0].indexOf("=");
@@ -1983,21 +2000,21 @@ function makeSeg()
});
var cn = `<div class="seg lstI expanded">
<div class="segin">
<input type="text" class="noslide" id="seg${lu}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lu}"/>
<input type="text" id="seg${lu}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lu}"/>
<table class="segt">
<tr>
<td width="38%">${isM?'Start X':'Start LED'}</td>
<td width="38%">${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>
</tr>
<tr>
<td><input class="noslide segn" id="seg${lu}s" type="number" min="0" max="${isM?mw-1:ledCount-1}" value="${ns}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><input class="noslide segn" id="seg${lu}e" type="number" min="0" max="${ct}" value="${ct}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><input class="segn" id="seg${lu}s" type="number" min="0" max="${isM?mw-1:ledCount-1}" value="${ns}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><input class="segn" id="seg${lu}e" type="number" min="0" max="${ct}" value="${ct}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><button class="btn btn-xs" onclick="setSeg(${lu});"><i class="icons bth-icon" id="segc${lu}">&#xe390;</i></button></td>
</tr>
<tr id="mkSYH" class="${isM?"":"hide"}"><td>Start Y</td><td>${cfg.comp.seglen?'Height':'Stop Y'}</td></tr>
<tr id="mkSYD" class="${isM?"":"hide"}">
<td><input class="noslide segn" id="seg${lu}sY" type="number" min="0" max="${mh-1}" value="0" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><input class="noslide segn" id="seg${lu}eY" type="number" min="0" max="${mh}" value="${isM?mh:1}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><input class="segn" id="seg${lu}sY" type="number" min="0" max="${mh-1}" value="0" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><input class="segn" id="seg${lu}eY" type="number" min="0" max="${mh}" value="${isM?mh:1}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
</tr>
</table>
<div class="h" id="seg${lu}len">${ledCount - ns} LEDs</div>
@@ -2122,7 +2139,7 @@ function makeP(i,pl)
<span class="checkmark"></span>
</label>
<div id="pl${i}o1" style="display:${rep>0?"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>
<div class="c">Repeat <input type="number" id="pl${i}rp" oninput="plR(${i})" max=127 min=0 value=${rep>0?rep:1}> times</div>
<div class="sel">End preset:<br>
<div class="sel-p"><select class="sel-ple" id="pl${i}selEnd" onchange="plR(${i})" data-val=${plJson[i].end?plJson[i].end:0}>
<option value="0">None</option>
@@ -2161,8 +2178,8 @@ ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
}
}
return `<input type="text" class="ptxt noslide ${i==0?'show':''}" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/>
<div class="c">Quick load label: <input type="text" class="stxt noslide" maxlength=2 value="${qlName(i)}" id="p${i}ql" autocomplete="off"/></div>
return `<input type="text" class="ptxt ${i==0?'show':''}" id="p${i}txt" autocomplete="off" maxlength=32 value="${(i>0)?pName(i):""}" placeholder="Enter name..."/>
<div class="c">Quick load label: <input type="text" class="stxt" 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">
@@ -2173,9 +2190,9 @@ ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
<span class="checkmark"></span>
</label>
</div>
<div class="po2" id="p${i}o2">API command<br><textarea class="apitxt noslide" id="p${i}api"></textarea></div>
<div class="po2" id="p${i}o2">API command<br><textarea class="apitxt" id="p${i}api"></textarea></div>
<div class="po1" id="p${i}o1">${content}</div>
<div class="c m6">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 m6">Save to ID <input 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-p" onclick="saveP(${i},${pl})"><i class="icons btn-icon">&#xe390;</i>Save</button>
${(i>0)?'<button class="btn btn-p" id="p'+i+'del" onclick="delP('+i+')"><i class="icons btn-icon">&#xe037;</i>Delete':'<button class="btn btn-p" onclick="resetPUtil()">Cancel'}</button>
@@ -2220,8 +2237,8 @@ function makePlEntry(p,i)
<td class="c">#${i+1}</td>
</tr>
<tr>
<td class="c" width="40%"><input class="noslide segn" type="number" placeholder="Duration" max=6553.0 min=0.2 step=0.1 oninput="pleDur(${p},${i},this)" value="${plJson[p].dur[i]/10.0}">s</td>
<td class="c" width="40%"><input class="noslide segn" type="number" placeholder="Transition" max=65.0 min=0.0 step=0.1 oninput="pleTr(${p},${i},this)" value="${plJson[p].transition[i]/10.0}">s</td>
<td class="c" width="40%"><input class="segn" type="number" placeholder="Duration" max=6553.0 min=0.2 step=0.1 oninput="pleDur(${p},${i},this)" value="${plJson[p].dur[i]/10.0}">s</td>
<td class="c" width="40%"><input class="segn" type="number" placeholder="Transition" max=65.0 min=0.0 step=0.1 oninput="pleTr(${p},${i},this)" value="${plJson[p].transition[i]/10.0}">s</td>
<td class="c"><button class="btn btn-pl-del" onclick="delPl(${p},${i})"><i class="icons btn-icon">&#xe037;</i></button></div></td>
</tr>
</table>
@@ -2513,7 +2530,7 @@ function setLor(i)
function setPreset(i)
{
var obj = {"ps":i};
if (pJson && pJson[i] && (!pJson[i].win || pJson[i].win.indexOf("Please") <= 0)) {
if (!isPlaylist(i) && pJson && pJson[i] && (!pJson[i].win || pJson[i].win.indexOf("Please") <= 0)) {
// we will send the complete preset content as to avoid delay introduced by
// async nature of applyPreset() and having to read the preset from file system.
obj = {"pd":i}; // use "pd" instead of "ps" to indicate that we are sending the preset content directly
@@ -3156,7 +3173,7 @@ function hasIroClass(classList)
}
return false;
}
//required by rangetouch.js
function lock(e)
{
if (pcMode) return;
@@ -3170,7 +3187,7 @@ function lock(e)
_C.classList.toggle('smooth', !(locked = true));
}
//required by rangetouch.js
function move(e)
{
if(!locked || pcMode) return;