Search bar and palettes preview (#1637)

* Fix swipe behavior on firefox when clicking on palette selection input

When clicking on the palette select element on firefox, it would often swipe to the next tab due to a bug in firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1410816

* Update UI .h files and remove console log

* Fix indentation in requestJson

* Add palette preview
Add palette and effects search

* Add palette preview
Add palette and effects search

* Add palette preview

Add palette and effects search

* Add palette preview

Add palette and effects search

* Fix error with background image

* Add missing palettes

* Add expiration to cached palette data

* Remove extra console.log

* bug fixes for palettes

* Rename "Default" effect back to "Solid"

* Fix scrolling issue when selecting an effect

* Add sticky default and sticky selected item

* Change checkboxes for radio button

* build html .h files

* Design Iteration 1

* Palette preview style changes

* Add button for clearing search field

* Use version ID for caching palette data rather than 24h expiration

Co-authored-by: Aircoookie <dev.aircoookie@gmail.com>
This commit is contained in:
Christophe Gagnier 2021-03-18 18:59:56 -04:00 committed by GitHub
parent ba4c3e3852
commit 4a20f43fbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 2881 additions and 2225 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@
.gitignore
.clang-format
node_modules
.idea

View File

@ -166,6 +166,14 @@ button {
color: var(--c-d);
}
.search-cancel-icon {
position: absolute;
right: 8px;
top: 9px;
cursor: pointer;
display: none;
}
.flr {
float: right;
cursor: pointer;
@ -299,7 +307,7 @@ button {
top: -1px;
z-index: 1;
margin-top: 1px;
width: 274px;
width: 272px;
margin: auto;
border-radius: 25px;
}
@ -308,10 +316,6 @@ button {
top: 28px;
}
#staytop2 {
top: 56px;
}
#fxb0 {
margin-bottom: 2px;
filter: drop-shadow(0 0 1px #000);
@ -548,6 +552,7 @@ input[type=range]:active + .sliderbubble {
font-size: 19px;
background-color: var(--c-3);
color: var(--c-f);
cursor: pointer;
border: 0px solid white;
border-radius: 25px;
transition-duration: 0.5s;
@ -764,7 +769,7 @@ input[type=number]::-webkit-outer-spin-button {
cursor: pointer;
}
.check {
.check, .radio {
display: inline-block;
position: relative;
padding-bottom: 32px;
@ -774,7 +779,7 @@ input[type=number]::-webkit-outer-spin-button {
}
.schkl {
padding: 2px 22px 0px 35px;
padding: 2px 5px 0px 35px;
margin: 0 0 0 2px;
}
@ -784,7 +789,13 @@ input[type=number]::-webkit-outer-spin-button {
margin-top: 8px;
}
.check input {
.fxchkl {
position: absolute;
top: 0px;
left: 8px;
}
.check input, .radio input {
position: absolute;
opacity: 0;
cursor: pointer;
@ -792,7 +803,7 @@ input[type=number]::-webkit-outer-spin-button {
width: 0;
}
.checkmark {
.checkmark, .radiomark {
position: absolute;
bottom: 0;
left: 0;
@ -802,6 +813,13 @@ input[type=number]::-webkit-outer-spin-button {
border-radius: 10px;
}
.radiomark {
height: 24px;
width: 24px;
border-radius: 50%;
background-color: transparent;
}
.schk {
top: 0;
}
@ -828,13 +846,13 @@ input[type=number]::-webkit-outer-spin-button {
background-color: var(--c-6);
}
.checkmark:after {
.checkmark:after, .radiomark:after {
content: "";
position: absolute;
display: none;
}
.check input:checked ~ .checkmark:after {
.check input:checked ~ .checkmark:after, .radio input:checked ~ .radiomark:after {
display: block;
}
@ -850,6 +868,16 @@ input[type=number]::-webkit-outer-spin-button {
transform: rotate(45deg);
}
.radio .radiomark:after {
width: 12px;
height: 12px;
top: 50%;
left: 50%;
margin: -6px;
border-radius: 50%;
background: var(--c-f);
}
.h {
font-size: 13px;
text-align: center;
@ -873,7 +901,93 @@ input[type=number]::-webkit-outer-spin-button {
border-radius: 20px;
text-align: left;
transition: background-color 0.5s;
filter: brightness(1);
filter: brightness(1);
}
.list {
position: relative;
transition: background-color 0.5s;
margin: auto auto 10px;
padding-bottom: 10px;
width: 230px;
}
.lstI {
overflow: hidden;
}
.fxbtn {
margin: 20px auto;
padding: 8px 0;
}
.lstI:hover {
background: var(--c-4);
}
.lstI:last-child {
border: none;
}
.lstI.sticky, .lstI.selected {
position: sticky;
z-index: 1;
}
#selectPalette .lstI.selected {
top: 27px;
bottom: -11px;
}
#selectPalette .lstI.sticky {
top: -11px;
}
.lstI.selected {
background: var(--c-5);
top: 95px;
bottom: -11px;
}
.lstI.sticky {
top: 57px;
}
.lstIname {
margin: 3px 0;
white-space: nowrap;
cursor: pointer;
font-size: 19px;
}
.lstIprev {
width: 100%;
height: 5px;
margin: auto;
position: absolute;
bottom: 0px;
left: 0px;
}
input[type="text"].search {
display: block;
width: 230px;
box-sizing: border-box;
padding: 8px 8px 9px 38px;
margin: 6px auto 0 auto;
text-align: left;
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' stroke='white'><ellipse fill-opacity='0' stroke-width='10' cx='35' cy='35' id='svg_1' rx='25' ry='25' /><line stroke-width='10' x1='48.5' y1='70.75' x2='92.5' y2='70.75' transform='rotate(45, 70.5, 70.75)' /></svg>")
no-repeat 10px 10px;
background-size: 20px;
background-color: var(--c-3);
}
input[type="text"].search:focus {
background-color: var(--c-4);
}
input[type="text"].search:not(:placeholder-shown) {
background-color: var(--c-5);
}
.pres {

View File

@ -88,13 +88,33 @@
<input id="hexc" type="text" class="noslide" onkeydown="hexEnter(this)" autocomplete="off" maxlength="8" />
<button id="hexcnf" class="xxs btn" onclick="fromHex();"><i class="icons btna-icon">&#xe390;</i></button>
</div>
<p class="labels">Color palette</p>
<div class="il">
<p class="labels">
<i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i>
<select class="btn sel" id="selectPalette" onchange="setPalette()">
<option>Default</option>
<option>Error!</option>
</select>
Color palette
</p>
<div class="il">
<div id="selectPalette" class="list">
<div class="lstI" data-id="0">
<label class="check schkl">
&nbsp;
<input type="radio" value="${palettes[i].id}" name="palette" onChange="setPalette()">
<span class="checkmark schk"></span>
</label>
<div class="lstIcontent">
<span class="lstIname">
Default
</span>
</div>
</div>
<div class="lstI">
<div class="lstIcontent">
<span class="lstIname">
Loading...
</span>
</div>
</div>
</div>
</div>
</div>
@ -118,13 +138,9 @@
</div>
</div>
<p class="labels">Effect mode</p>
<div class="staytop" id="staytop2">
<button class="btn" id="fxb0" onclick="setX(0);">Solid</button>
</div>
<div id="fxlist">
<div id="fxlist" class="list">
Loading...
</div>
<br>
</div>
<div id="Segments" class="tabcontent">

View File

@ -3,6 +3,7 @@ var loc = false, locip;
var noNewSegs = false;
var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, syncSend = false, syncTglRecv = true, isRgbw = false;
var whites = [0,0,0];
var selColors;
var expanded = [false];
var powered = [true];
var nlDur = 60, nlTar = 0;
@ -15,6 +16,7 @@ var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0;
var pcMode = false, pcModeA = false, lastw = 0;
var d = document;
const ranges = RangeTouch.setup('input[type="range"]', {});
var palettesData;
var pJson = {};
var pN = "", pI = 0;
var pmt = 1, pmtLS = 0, pmtLast = 0;
@ -157,7 +159,6 @@ function loadBg(iUrl) {
}
img.addEventListener('load', (event) => {
var a = parseFloat(cfg.theme.alpha.bg);
d.getElementById('staytop2').style.background = "transparent";
if (isNaN(a)) a = 0.6;
bg.style.opacity = a;
bg.style.backgroundImage = `url(${img.src})`;
@ -309,7 +310,6 @@ function cpBck() {
copyText.select();
copyText.setSelectionRange(0, 999999);
d.execCommand("copy");
showToast("Copied to clipboard!");
@ -567,6 +567,151 @@ function populateSegments(s)
d.getElementById('rsbtn').style.display = (segCount > 1) ? "inline":"none";
}
function populateEffects(effects)
{
var html = `<div class="searchbar"><input type="text" class="search" placeholder="Search" oninput="search(this)" />
<i class="icons search-cancel-icon" onclick="cancelSearch(this)">&#xe38f;</i></div>`;
effects.shift(); //remove solid
for (let i = 0; i < effects.length; i++) {
effects[i] = {id: parseInt(i)+1, name:effects[i]};
}
effects.sort(compare);
effects.unshift({
"id": 0,
"name": "Solid",
"class": "sticky"
});
for (let i = 0; i < effects.length; i++) {
html += generateListItemHtml(
'fx',
effects[i].id,
effects[i].name,
'setX',
'',
effects[i].class,
);
}
d.getElementById('fxlist').innerHTML=html;
}
function populatePalettes(palettes)
{
palettes.shift(); //remove default
for (let i = 0; i < palettes.length; i++) {
palettes[i] = {
"id": parseInt(i)+1,
"name": palettes[i]
};
}
palettes.sort(compare);
palettes.unshift({
"id": 0,
"name": "Default",
"class": "sticky"
});
var html = `<div class="searchbar"><input type="text" class="search" placeholder="Search" oninput="search(this)" />
<i class="icons search-cancel-icon" onclick="cancelSearch(this)">&#xe38f;</i></div>`;
for (let i = 0; i < palettes.length; i++) {
let previewCss = genPalPrevCss(palettes[i].id);
html += generateListItemHtml(
'palette',
palettes[i].id,
palettes[i].name,
'setPalette',
`<div class="lstIprev" style="${previewCss}"></div>`,
palettes[i].class,
);
}
d.getElementById('selectPalette').innerHTML=html;
}
function redrawPalPrev()
{
let palettes = d.querySelectorAll('#selectPalette .lstI');
for (let i = 0; i < palettes.length; i++) {
let id = palettes[i].dataset.id;
let lstPrev = palettes[i].querySelector('.lstIprev');
if (lstPrev) {
lstPrev.style = genPalPrevCss(id);
}
}
}
function genPalPrevCss(id)
{
if (!palettesData) {
return;
}
var paletteData = palettesData[id];
var previewCss = "";
if (!paletteData) {
return 'display: none';
}
// We need at least two colors for a gradient
if (paletteData.length == 1) {
paletteData[1] = paletteData[0];
if (Array.isArray(paletteData[1])) {
paletteData[1][0] = 255;
}
}
var gradient = [];
for (let j = 0; j < paletteData.length; j++) {
const element = paletteData[j];
let r;
let g;
let b;
let index = false;
if (Array.isArray(element)) {
index = element[0]/255*100;
r = element[1];
g = element[2];
b = element[3];
} else if (element == 'r') {
r = Math.random() * 255;
g = Math.random() * 255;
b = Math.random() * 255;
} else {
if (selColors) {
let pos = element[1] - 1;
r = selColors[pos][0];
g = selColors[pos][1];
b = selColors[pos][2];
}
}
if (index === false) {
index = j / paletteData.length * 100;
}
gradient.push(`rgb(${r},${g},${b}) ${index}%`);
}
return `background: linear-gradient(to right,${gradient.join()});`;
}
function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', extraClass = '')
{
return `<div class="lstI btn fxbtn ${extraClass}" data-id="${id}" onClick="${clickAction}(${id})">
<label class="radio fxchkl">
<input type="radio" value="${id}" name="${listName}">
<span class="radiomark"></span>
</label>
<span class="lstIname">
${name}
</span>
${extraHtml}
</div>`;
}
function btype(b){
switch (b) {
case 32: return "ESP32";
@ -711,15 +856,12 @@ function updateUI()
d.getElementById('buttonNl').className = (nlA) ? "active":"";
d.getElementById('buttonSync').className = (syncSend) ? "active":"";
d.getElementById('fxb' + selectedFx).style.backgroundColor = "var(--c-6)";
updateTrail(d.getElementById('sliderBri'));
updateTrail(d.getElementById('sliderSpeed'));
updateTrail(d.getElementById('sliderIntensity'));
updateTrail(d.getElementById('sliderW'));
if (isRgbw) d.getElementById('wwrap').style.display = "block";
var spal = d.getElementById("selectPalette");
spal.style.backgroundColor = (spal.selectedIndex > 0) ? "var(--c-6)":"var(--c-3)";
updatePA();
updateHex();
updateRgb();
@ -774,7 +916,7 @@ function requestJson(command, rinfo = true, verbose = true) {
})
.then(res => {
if (!res.ok) {
showErrorToast();
showErrorToast();
}
return res.json();
})
@ -783,54 +925,63 @@ function requestJson(command, rinfo = true, verbose = true) {
jsonTimeout = null;
clearErrorToast();
d.getElementById('connind').style.backgroundColor = "#070";
if (!json) showToast('Empty response', true);
if (json.success) return;
if (!json) {
showToast('Empty response', true);
}
if (json.success) {
return;
}
var s = json;
if (!command || rinfo) {
if (!rinfo) {
pmt = json.info.fs.pmt;
if (pmt != pmtLS || pmt == 0) {
setTimeout(loadPresets,99);
}
else populatePresets(true);
pmtLast = pmt;
var x='',y='<option value="0">Default</option>';
json.effects.shift(); //remove solid
for (let i = 0; i < json.effects.length; i++) json.effects[i] = {id: parseInt(i)+1, name:json.effects[i]};
json.effects.sort(compare);
for (let i = 0; i < json.effects.length; i++) {
x += `<button class="btn${(i==0)?" first":""}" id="fxb${json.effects[i].id}" onclick="setX(${json.effects[i].id});">${json.effects[i].name}</button><br>`;
}
json.palettes.shift(); //remove default
for (let i = 0; i < json.palettes.length; i++) json.palettes[i] = {"id": parseInt(i)+1, "name":json.palettes[i]};
json.palettes.sort(compare);
for (let i = 0; i < json.palettes.length; i++) {
y += `<option value="${json.palettes[i].id}">${json.palettes[i].name}</option>`;
}
e1.innerHTML=x; e2.innerHTML=y;
}
if (!command || rinfo) {
if (!rinfo) {
pmt = json.info.fs.pmt;
if (pmt != pmtLS || pmt == 0) {
setTimeout(loadPresets,99);
}
else {
populatePresets(true);
}
pmtLast = pmt;
populateEffects(json.effects);
populatePalettes(json.palettes);
}
var info = json.info;
var name = info.name;
d.getElementById('namelabel').innerHTML = name;
if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)";
if (info.live) name = "(Live) " + name;
if (loc) name = "(L) " + name;
if (name === "Dinnerbone") {
d.documentElement.style.transform = "rotate(180deg)";
}
if (info.live) {
name = "(Live) " + name;
}
if (loc) {
name = "(L) " + name;
}
d.title = name;
isRgbw = info.leds.wv;
ledCount = info.leds.count;
syncTglRecv = info.str;
maxSeg = info.leds.maxseg;
pmt = info.fs.pmt;
if (!command && pmt != pmtLast) setTimeout(loadPresets,99);
pmtLast = pmt;
pmt = info.fs.pmt;
if (!command && pmt != pmtLast) {
setTimeout(loadPresets,99);
}
pmtLast = pmt;
d.getElementById('buttonNodes').style.display = (info.ndc > 0 && window.innerWidth > 770) ? "block":"none";
lastinfo = info;
if (isInfo) populateInfo(info);
lastinfo = info;
if (isInfo) {
populateInfo(info);
}
s = json.state;
displayRover(info, s);
if (!rinfo) loadPalettesData();
}
isOn = s.on;
d.getElementById('sliderBri').value= s.bri;
nlA = s.nl.on;
@ -839,7 +990,7 @@ 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) ? false : true;
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;
@ -857,6 +1008,8 @@ function requestJson(command, rinfo = true, verbose = true) {
updateUI();
return;
}
selColors = i.col;
var cd = d.getElementById('csl').children;
for (let e = 2; e >= 0; e--)
{
@ -869,26 +1022,54 @@ function requestJson(command, rinfo = true, verbose = true) {
d.getElementById('sliderSpeed').value = i.sx;
d.getElementById('sliderIntensity').value = i.ix;
d.getElementById('fxb' + selectedFx).style.backgroundColor = "var(--c-3)";
// Effects
e1.querySelector(`input[name="fx"][value="${i.fx}"]`).checked = true;
var selElement = e1.querySelector('.selected');
if (selElement) {
selElement.classList.remove('selected')
}
var selectedEffect = e1.querySelector(`.lstI[data-id="${i.fx}"]`);
selectedEffect.classList.add('selected');
selectedFx = i.fx;
e2.value = i.pal;
if (!command) d.getElementById('Effects').scrollTop = d.getElementById('fxb' + selectedFx).offsetTop - d.getElementById('Effects').clientHeight/1.8;
// Palettes
e2.querySelector(`input[name="palette"][value="${i.pal}"]`).checked = true;
selElement = e2.querySelector('.selected');
if (selElement) {
selElement.classList.remove('selected')
}
e2.querySelector(`.lstI[data-id="${i.pal}"]`).classList.add('selected');
if (!command) {
selectedEffect.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
});
}
if (s.error && s.error != 0) {
var errstr = "";
switch (s.error) {
case 10: errstr = "Could not mount filesystem!"; break;
case 11: errstr = "Not enough space to save preset!"; break;
case 12: errstr = "The requested preset does not exist."; break;
case 19: errstr = "A filesystem error has occured."; break;
}
showToast('Error ' + s.error + ": " + errstr, true);
}
var errstr = "";
switch (s.error) {
case 10:
errstr = "Could not mount filesystem!";
break;
case 11:
errstr = "Not enough space to save preset!";
break;
case 12:
errstr = "The requested preset does not exist.";
break;
case 19:
errstr = "A filesystem error has occured.";
break;
}
showToast('Error ' + s.error + ": " + errstr, true);
}
updateUI();
})
.catch(function (error) {
showToast(error, true);
console.log(error);
console.log(error);
});
}
@ -1104,14 +1285,35 @@ function setSegBri(s){
requestJson(obj);
}
function setX(ind) {
function setX(ind = null) {
if (ind === null) {
ind = parseInt(d.querySelector('#fxlist input[name="fx"]:checked').value);
} else {
d.querySelector(`#fxlist input[name="fx"][value="${ind}`).checked = true;
}
var selElement = d.querySelector('#fxlist .selected');
if (selElement) {
selElement.classList.remove('selected')
}
d.querySelector(`#fxlist .lstI[data-id="${ind}"]`).classList.add('selected');
var obj = {"seg": {"fx": parseInt(ind)}};
requestJson(obj);
}
function setPalette()
function setPalette(paletteId = null)
{
var obj = {"seg": {"pal": parseInt(d.getElementById('selectPalette').value)}};
if (paletteId === null) {
paletteId = parseInt(d.querySelector('#selectPalette input[name="palette"]:checked').value);
} else {
d.querySelector(`#selectPalette input[name="palette"][value="${paletteId}`).checked = true;
}
var selElement = d.querySelector('#selectPalette .selected');
if (selElement) {
selElement.classList.remove('selected')
}
d.querySelector(`#selectPalette .lstI[data-id="${paletteId}"]`).classList.add('selected');
var obj = {"seg": {"pal": paletteId}};
requestJson(obj);
}
@ -1227,6 +1429,7 @@ function selectSlot(b) {
updateTrail(d.getElementById('sliderW'));
updateHex();
updateRgb();
redrawPalPrev();
}
var lasth = 0;
@ -1353,6 +1556,88 @@ function rSegs()
requestJson(obj);
}
function loadPalettesData()
{
if (palettesData) return;
const lsKey = "wledPalx";
var palettesDataJson = localStorage.getItem(lsKey);
if (palettesDataJson) {
try {
palettesDataJson = JSON.parse(palettesDataJson);
var d = new Date();
if (palettesDataJson && palettesDataJson.vid == lastinfo.vid) {
palettesData = palettesDataJson.p;
return;
}
} catch (e) {}
}
palettesData = {};
getPalettesData(0, function() {
localStorage.setItem(lsKey, JSON.stringify({
p: palettesData,
vid: lastinfo.vid
}));
redrawPalPrev();
});
}
function getPalettesData(page, callback)
{
var url = `/json/palx?page=${page}`;
if (loc) {
url = `http://${locip}${url}`;
}
fetch(url, {
method: 'get',
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
.then(res => {
if (!res.ok) {
showErrorToast();
}
return res.json();
})
.then(json => {
palettesData = Object.assign({}, palettesData, json.p);
if (page < json.m) {
getPalettesData(page + 1, callback);
} else {
callback();
}
})
.catch(function (error) {
showToast(error, true);
console.log(error);
presetError(false);
});
}
function search(searchField) {
var searchText = searchField.value.toUpperCase();
searchField.parentElement.getElementsByClassName('search-cancel-icon')[0].style.display = (searchText.length < 1)?"none":"inline";
var elements = searchField.parentElement.parentElement.querySelectorAll('.lstI');
for (i = 0; i < elements.length; i++) {
var item = elements[i];
var itemText = item.querySelector('.lstIname').innerText.toUpperCase();
if (itemText.indexOf(searchText) > -1) {
item.style.display = "";
} else {
item.style.display = "none";
}
}
}
function cancelSearch(ic) {
var searchField = ic.parentElement.getElementsByClassName('search')[0];
searchField.value = "";
search(searchField);
searchField.focus();
}
function expand(i,a)
{
if (!a) expanded[i] = !expanded[i];

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,7 @@
#include "wled.h"
#include "palettes.h"
/*
* JSON API (De)serialization
*/
@ -57,7 +59,8 @@ void deserializeSegment(JsonObject elem, byte it)
if (sz == 0) continue; //do nothing on empty array
byte cp = copyArray(colX, rgbw, 4);
if (cp == 1 && rgbw[0] == 0) seg.setColor(i, 0, id);
if (cp == 1 && rgbw[0] == 0)
seg.setColor(i, 0, id);
colValid = true;
}
@ -538,6 +541,153 @@ void serializeInfo(JsonObject root)
root["mac"] = escapedMac;
}
void setPaletteColors(JsonArray json, CRGBPalette16 palette)
{
for (int i = 0; i < 16; i++) {
JsonArray colors = json.createNestedArray();
CRGB color = palette[i];
colors.add((((float)i / (float)16) * 255));
colors.add(color.red);
colors.add(color.green);
colors.add(color.blue);
}
}
void setPaletteColors(JsonArray json, byte* tcp)
{
TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(tcp);
TRGBGradientPaletteEntryUnion u;
// Count entries
uint16_t count = 0;
do {
u = *(ent + count);
count++;
} while ( u.index != 255);
u = *ent;
int indexstart = 0;
while( indexstart < 255) {
indexstart = u.index;
JsonArray colors = json.createNestedArray();
colors.add(u.index);
colors.add(u.r);
colors.add(u.g);
colors.add(u.b);
ent++;
u = *ent;
}
}
void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
{
#ifdef ESP8266
int itemPerPage = 5;
#else
int itemPerPage = 8;
#endif
int page = 0;
if (request->hasParam("page")) {
page = request->getParam("page")->value().toInt();
}
int palettesCount = strip.getPaletteCount();
int maxPage = (palettesCount -1) / itemPerPage;
if (page > maxPage) page = maxPage;
int start = itemPerPage * page;
int end = start + itemPerPage;
if (end >= palettesCount) end = palettesCount;
root[F("m")] = maxPage;
JsonObject palettes = root.createNestedObject("p");
for (int i = start; i < end; i++) {
JsonArray curPalette = palettes.createNestedArray(String(i));
CRGB prim;
CRGB sec;
CRGB ter;
switch (i) {
case 0: //default palette
setPaletteColors(curPalette, PartyColors_p);
break;
case 1: //random
curPalette.add("r");
curPalette.add("r");
curPalette.add("r");
curPalette.add("r");
break;
case 2: //primary color only
curPalette.add(F("c1"));
break;
case 3: //primary + secondary
curPalette.add(F("c1"));
curPalette.add(F("c1"));
curPalette.add(F("c2"));
curPalette.add(F("c2"));
break;
case 4: //primary + secondary + tertiary
curPalette.add(F("c3"));
curPalette.add(F("c2"));
curPalette.add(F("c1"));
break;
case 5: {//primary + secondary (+tert if not off), more distinct
curPalette.add(F("c1"));
curPalette.add(F("c1"));
curPalette.add(F("c1"));
curPalette.add(F("c1"));
curPalette.add(F("c1"));
curPalette.add(F("c2"));
curPalette.add(F("c2"));
curPalette.add(F("c2"));
curPalette.add(F("c2"));
curPalette.add(F("c2"));
curPalette.add(F("c3"));
curPalette.add(F("c3"));
curPalette.add(F("c3"));
curPalette.add(F("c3"));
curPalette.add(F("c3"));
curPalette.add(F("c1"));
break;}
case 6: //Party colors
setPaletteColors(curPalette, PartyColors_p);
break;
case 7: //Cloud colors
setPaletteColors(curPalette, CloudColors_p);
break;
case 8: //Lava colors
setPaletteColors(curPalette, LavaColors_p);
break;
case 9: //Ocean colors
setPaletteColors(curPalette, OceanColors_p);
break;
case 10: //Forest colors
setPaletteColors(curPalette, ForestColors_p);
break;
case 11: //Rainbow colors
setPaletteColors(curPalette, RainbowColors_p);
break;
case 12: //Rainbow stripe colors
setPaletteColors(curPalette, RainbowStripeColors_p);
break;
default:
if (i < 13) {
break;
}
byte tcp[72];
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[i - 13])), 72);
setPaletteColors(curPalette, tcp);
break;
}
}
}
void serializeNodes(JsonObject root)
{
JsonArray nodes = root.createNestedArray("nodes");
@ -564,6 +714,7 @@ void serveJson(AsyncWebServerRequest* request)
else if (url.indexOf("info") > 0) subJson = 2;
else if (url.indexOf("si") > 0) subJson = 3;
else if (url.indexOf("nodes") > 0) subJson = 4;
else if (url.indexOf("palx") > 0) subJson = 5;
else if (url.indexOf("live") > 0) {
serveLiveLeds(request);
return;
@ -592,10 +743,12 @@ void serveJson(AsyncWebServerRequest* request)
serializeInfo(doc); break;
case 4: //node list
serializeNodes(doc); break;
case 5: //palettes
serializePalettes(doc, request); break;
default: //all
JsonObject state = doc.createNestedObject("state");
serializeState(state);
JsonObject info = doc.createNestedObject("info");
JsonObject info = doc.createNestedObject("info");
serializeInfo(info);
if (subJson != 3)
{